
//此源码被清华学神尹成大魔王专业翻译分析并修改
//尹成QQ77025077
//尹成微信18510341407
//尹成所在QQ群721929980
//尹成邮箱 yinc13@mails.tsinghua.edu.cn
//尹成毕业于清华大学,微软区块链领域全球最有价值专家
//https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
/*
 *@文件
 *@eos/license中定义的版权
 *@defgroup eosclienttool eosio命令行客户端引用
 *@brief工具，用于从@ref nodeos发送事务和查询状态
 *@ingroup eosclienttool公司
 **/


/*
  @defgroup eosclienttool

  @部分克莱奥的简介

  ‘cleos’是一个命令行工具，它与@ref nodeos公开的rest api接口。要使用“cleos”，您需要
  运行“nodeos”的本地副本，并将其配置为加载“eosio:：chain_api_plugin”。

   cleos包含所有命令的文档。对于cleos已知的所有命令的列表，只需运行它而不带参数：
` `
$ /克利奥斯
EOSIO客户端的命令行接口
用法：programs/cleos/cleos[选项]子命令

选项：
  -h，--help打印此帮助消息并退出
  -u，-url text=http://localhost:8888/
                              运行nodeos的http/https URL
  --钱包url text=http://localhost:8888/
                              运行keosd的http/https URL
  -r，--header传递特定的HTTP头，重复此选项传递多个头
  -n，-no verify使用https时不验证对等证书
  -v，-verbose输出出错时的详细操作

子命令：
  版本检索版本信息
  创建创建各种项目，在区块链上下
  从区块链中获取各种项目和信息
  设置或更新区块链状态
  将代币从账户转移到账户
  网络与本地P2P网络连接交互
  钱包与本地钱包互动
  签署签署交易
  推送任意交易到区块链
  multisig multisig合同命令

` `
要获得任何特定子命令的帮助，也可以不带参数运行它：
` `
美元/cleos创建
创建各种项目，在区块链上下
用法：/cleos create子命令

子命令：
  密钥创建新的密钥对并打印公钥和私钥
  账户在区块链上创建一个新账户（假设系统合同不限制RAM的使用）

美元/欧元创建帐户
在区块链上创建一个新账户（假设系统合同不限制RAM的使用）
用法：/cleos create account[选项]creator name ownerkey activekey

Positionals：
  创建者文本创建新帐户的帐户的名称
  名称文本新帐户的名称
  owner key text新帐户的所有者公钥
  ActiveKey文本新帐户的活动公钥

选项：
  -x，-expiration设置事务过期前的时间（秒），默认为30s
  -f，-force unique强制事务是唯一的。这将消耗额外的带宽，并消除对多次意外发布同一事务的任何保护。
  -s，-skip-sign指定是否应使用未锁定的钱包密钥签署交易
  -d，-don broadcast不要向网络广播事务（只打印到stdout）
  -p，-权限文本…要授权的帐户和权限级别，如“account@permission”（默认为“creator@active”）。
` `
**/


#include <pwd.h>
#include <string>
#include <vector>
#include <regex>
#include <iostream>
#include <fc/crypto/hex.hpp>
#include <fc/variant.hpp>
#include <fc/io/datastream.hpp>
#include <fc/io/json.hpp>
#include <fc/io/console.hpp>
#include <fc/exception/exception.hpp>
#include <fc/variant_object.hpp>

#include <eosio/chain/name.hpp>
#include <eosio/chain/config.hpp>
#include <eosio/chain/wast_to_wasm.hpp>
#include <eosio/chain/trace.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio/chain/contract_types.hpp>

#pragma push_macro("N")
#undef N

#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <boost/process/spawn.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/range/algorithm/sort.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/algorithm/string/classification.hpp>

#pragma pop_macro("N")

#include <Inline/BasicTypes.h>
#include <IR/Module.h>
#include <IR/Validate.h>
#include <WASM/WASM.h>
#include <Runtime/Runtime.h>

#include <fc/io/fstream.hpp>

#include "CLI11.hpp"
#include "help_text.hpp"
#include "localize.hpp"
#include "config.hpp"
#include "httpc.hpp"

using namespace std;
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::client::help;
using namespace eosio::client::http;
using namespace eosio::client::localize;
using namespace eosio::client::config;
using namespace boost::filesystem;

FC_DECLARE_EXCEPTION( explained_exception, 9000000, "explained exception, see error log" );
FC_DECLARE_EXCEPTION( localized_exception, 10000000, "an error occured" );
#define EOSC_ASSERT( TEST, ... ) \
  FC_EXPAND_MACRO( \
    FC_MULTILINE_MACRO_BEGIN \
      if( UNLIKELY(!(TEST)) ) \
      {                                                   \
        std::cerr << localized( __VA_ARGS__ ) << std::endl;  \
        FC_THROW_EXCEPTION( explained_exception, #TEST ); \
      }                                                   \
    FC_MULTILINE_MACRO_END \
  )

//从keosd's main.cpp复制意大利面
bfs::path determine_home_directory()
{
   bfs::path home;
   struct passwd* pwd = getpwuid(getuid());
   if(pwd) {
      home = pwd->pw_dir;
   }
   else {
      home = getenv("HOME");
   }
   if(home.empty())
      home = "./";
   return home;
}

string url = "http://127.0.0.1:8888/“；
string default_wallet_url = "unix://“+（determine_home_directory（）/”eosio wallet“/（string（key_store_executable_name）+”.sock”）.string（）；
string wallet_url; //设置为“主”中的默认“钱包”URL
bool no_verify = false;
vector<string> headers;

auto   tx_expiration = fc::seconds(30);
const fc::microseconds abi_serializer_max_time = fc::seconds(10); //对客户端序列化没有风险，需要很长时间
string tx_ref_block_num_or_id;
bool   tx_force_unique = false;
bool   tx_dont_broadcast = false;
bool   tx_return_packed = false;
bool   tx_skip_sign = false;
bool   tx_print_json = false;
bool   print_request = false;
bool   print_response = false;
bool   no_auto_keosd = false;

uint8_t  tx_max_cpu_usage = 0;
uint32_t tx_max_net_usage = 0;

uint32_t delaysec = 0;

vector<string> tx_permission;

eosio::client::http::http_context context;

void add_standard_transaction_options(CLI::App* cmd, string default_permission = "") {
   CLI::callback_t parse_expiration = [](CLI::results_t res) -> bool {
      double value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
         return false;
      }

      tx_expiration = fc::seconds(static_cast<uint64_t>(value_s));
      return true;
   };

   cmd->add_option("-x,--expiration", parse_expiration, localized("set the time in seconds before a transaction expires, defaults to 30s"));
   cmd->add_flag("-f,--force-unique", tx_force_unique, localized("force the transaction to be unique. this will consume extra bandwidth and remove any protections against accidently issuing the same transaction multiple times"));
   cmd->add_flag("-s,--skip-sign", tx_skip_sign, localized("Specify if unlocked wallet keys should be used to sign transaction"));
   cmd->add_flag("-j,--json", tx_print_json, localized("print result as json"));
   cmd->add_flag("-d,--dont-broadcast", tx_dont_broadcast, localized("don't broadcast transaction to the network (just print to stdout)"));
   cmd->add_flag("--return-packed", tx_return_packed, localized("used in conjunction with --dont-broadcast to get the packed transaction"));
   cmd->add_option("-r,--ref-block", tx_ref_block_num_or_id, (localized("set the reference block num or block id used for TAPOS (Transaction as Proof-of-Stake)")));

   string msg = "An account and permission level to authorize, as in 'account@permission'";
   if(!default_permission.empty())
      msg += " (defaults to '" + default_permission + "')";
   cmd->add_option("-p,--permission", tx_permission, localized(msg.c_str()));

   cmd->add_option("--max-cpu-usage-ms", tx_max_cpu_usage, localized("set an upper limit on the milliseconds of cpu usage budget, for the execution of the transaction (defaults to 0 which means no limit)"));
   cmd->add_option("--max-net-usage", tx_max_net_usage, localized("set an upper limit on the net usage budget, in bytes, for the transaction (defaults to 0 which means no limit)"));

   cmd->add_option("--delay-sec", delaysec, localized("set the delay_sec seconds, defaults to 0s"));
}

vector<chain::permission_level> get_account_permissions(const vector<string>& permissions) {
   auto fixedPermissions = permissions | boost::adaptors::transformed([](const string& p) {
      vector<string> pieces;
      split(pieces, p, boost::algorithm::is_any_of("@"));
      if( pieces.size() == 1 ) pieces.push_back( "active" );
      return chain::permission_level{ .actor = pieces[0], .permission = pieces[1] };
   });
   vector<chain::permission_level> accountPermissions;
   boost::range::copy(fixedPermissions, back_inserter(accountPermissions));
   return accountPermissions;
}

vector<chain::permission_level> get_account_permissions(const vector<string>& permissions, const chain::permission_level& default_permission) {
   if (permissions.empty())
      return vector<chain::permission_level>{default_permission};
   else
      return get_account_permissions(tx_permission);
}

template<typename T>
fc::variant call( const std::string& url,
                  const std::string& path,
                  const T& v ) {
   try {
      auto sp = std::make_unique<eosio::client::http::connection_param>(context, parse_url(url) + path, no_verify ? false : true, headers);
      return eosio::client::http::do_http_call(*sp, fc::variant(v), print_request, print_response );
   }
   catch(boost::system::system_error& e) {
      if(url == ::url)
         std::cerr << localized("Failed to connect to nodeos at ${u}; is nodeos running?", ("u", url)) << std::endl;
      else if(url == ::wallet_url)
         std::cerr << localized("Failed to connect to keosd at ${u}; is keosd running?", ("u", url)) << std::endl;
      throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, e.what())});
   }
}

template<typename T>
fc::variant call( const std::string& path,
                  const T& v ) { return call( url, path, fc::variant(v) ); }

template<>
fc::variant call( const std::string& url,
                  const std::string& path) { return call( url, path, fc::variant() ); }

eosio::chain_apis::read_only::get_info_results get_info() {
   return call(url, get_info_func).as<eosio::chain_apis::read_only::get_info_results>();
}

string generate_nonce_string() {
   return fc::to_string(fc::time_point::now().time_since_epoch().count());
}

chain::action generate_nonce_action() {
   return chain::action( {}, config::null_account_name, "nonce", fc::raw::pack(fc::time_point::now().time_since_epoch().count()));
}

void prompt_for_wallet_password(string& pw, const string& name) {
   if(pw.size() == 0 && name != "SecureEnclave") {
      std::cout << localized("password: ");
      fc::set_console_echo(false);
      std::getline( std::cin, pw, '\n' );
      fc::set_console_echo(true);
   }
}

fc::variant determine_required_keys(const signed_transaction& trx) {
//要更好地检查错误
//WDUDP（（Trx））；
   const auto& public_keys = call(wallet_url, wallet_public_keys);
   auto get_arg = fc::mutable_variant_object
           ("transaction", (transaction)trx)
           ("available_keys", public_keys);
   const auto& required_keys = call(get_required_keys, get_arg);
   return required_keys["required_keys"];
}

void sign_transaction(signed_transaction& trx, fc::variant& required_keys, const chain_id_type& chain_id) {
   fc::variants sign_args = {fc::variant(trx), required_keys, fc::variant(chain_id)};
   const auto& signed_trx = call(wallet_url, wallet_sign_trx, sign_args);
   trx = signed_trx.as<signed_transaction>();
}

fc::variant push_transaction( signed_transaction& trx, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
   auto info = get_info();

if (trx.signatures.size() == 0) { //5445如果已经签名，则无法更改txn内容
      trx.expiration = info.head_block_time + tx_expiration;

//设置tapos，如果用户未指定，则默认为最后一个不可逆块。
      block_id_type ref_block_id = info.last_irreversible_block_id;
      try {
         fc::variant ref_block;
         if (!tx_ref_block_num_or_id.empty()) {
            ref_block = call(get_block_func, fc::mutable_variant_object("block_num_or_id", tx_ref_block_num_or_id));
            ref_block_id = ref_block["id"].as<block_id_type>();
         }
      } EOS_RETHROW_EXCEPTIONS(invalid_ref_block_exception, "Invalid reference block num or id: ${block_num_or_id}", ("block_num_or_id", tx_ref_block_num_or_id));
      trx.set_reference_block(ref_block_id);

      if (tx_force_unique) {
         trx.context_free_actions.emplace_back( generate_nonce_action() );
      }

      trx.max_cpu_usage_ms = tx_max_cpu_usage;
      trx.max_net_usage_words = (tx_max_net_usage + 7)/8;
      trx.delay_sec = delaysec;
   }

   if (!tx_skip_sign) {
      auto required_keys = determine_required_keys(trx);
      sign_transaction(trx, required_keys, info.chain_id);
   }

   if (!tx_dont_broadcast) {
      return call(push_txn_func, packed_transaction(trx, compression));
   } else {
      if (!tx_return_packed) {
        return fc::variant(trx);
      } else {
        return fc::variant(packed_transaction(trx, compression));
      }
   }
}

fc::variant push_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none ) {
   signed_transaction trx;
   trx.actions = std::forward<decltype(actions)>(actions);

   return push_transaction(trx, extra_kcpu, compression);
}

void print_action( const fc::variant& at ) {
   const auto& receipt = at["receipt"];
   auto receiver = receipt["receiver"].as_string();
   const auto& act = at["act"].get_object();
   auto code = act["account"].as_string();
   auto func = act["name"].as_string();
   auto args = fc::json::to_string( act["data"] );
   auto console = at["console"].as_string();

   /*
   如果（code==“eosio”&&func==“setcode”）
      args=args.substr（40）+“..”；
   if（name（code）==config:：system_account_name&&func==“setabi”）
      args=args.substr（40）+“..”；
   **/

   if( args.size() > 100 ) args = args.substr(0,100) + "...";
   cout << "#" << std::setw(14) << right << receiver << " <= " << std::setw(28) << std::left << (code +"::" + func) << " " << args << "\n";
   if( console.size() ) {
      std::stringstream ss(console);
      string line;
      std::getline( ss, line );
      cout << ">> " << line << "\n";
   }
}

//ABI序列化程序的解析程序，用于对多签名合同中建议的事务中的操作进行解码
auto abi_serializer_resolver = [](const name& account) -> optional<abi_serializer> {
   static unordered_map<account_name, optional<abi_serializer> > abi_cache;
   auto it = abi_cache.find( account );
   if ( it == abi_cache.end() ) {
      auto result = call(get_abi_func, fc::mutable_variant_object("account_name", account));
      auto abi_results = result.as<eosio::chain_apis::read_only::get_abi_results>();

      optional<abi_serializer> abis;
      if( abi_results.abi.valid() ) {
         abis.emplace( *abi_results.abi, abi_serializer_max_time );
      } else {
         std::cerr << "ABI for contract " << account.to_string() << " not found. Action data will be shown in hex only." << std::endl;
      }
      abi_cache.emplace( account, abis );

      return abis;
   }

   return it->second;
};

bytes variant_to_bin( const account_name& account, const action_name& action, const fc::variant& action_args_var ) {
   auto abis = abi_serializer_resolver( account );
   FC_ASSERT( abis.valid(), "No ABI found for ${contract}", ("contract", account));

   auto action_type = abis->get_action_type( action );
   FC_ASSERT( !action_type.empty(), "Unknown action ${action} in contract ${contract}", ("action", action)( "contract", account ));
   return abis->variant_to_binary( action_type, action_args_var, abi_serializer_max_time );
}

fc::variant bin_to_variant( const account_name& account, const action_name& action, const bytes& action_args) {
   auto abis = abi_serializer_resolver( account );
   FC_ASSERT( abis.valid(), "No ABI found for ${contract}", ("contract", account));

   auto action_type = abis->get_action_type( action );
   FC_ASSERT( !action_type.empty(), "Unknown action ${action} in contract ${contract}", ("action", action)( "contract", account ));
   return abis->binary_to_variant( action_type, action_args, abi_serializer_max_time );
}

fc::variant json_from_file_or_string(const string& file_or_str, fc::json::parse_type ptype = fc::json::legacy_parser)
{
   regex r("^[ \t]*[\{\[]");
   if ( !regex_search(file_or_str, r) && fc::is_regular_file(file_or_str) ) {
      return fc::json::from_file(file_or_str, ptype);
   } else {
      return fc::json::from_string(file_or_str, ptype);
   }
}

bytes json_or_file_to_bin( const account_name& account, const action_name& action, const string& data_or_filename ) {
   fc::variant action_args_var;
   if( !data_or_filename.empty() ) {
      try {
         action_args_var = json_from_file_or_string(data_or_filename, fc::json::relaxed_parser);
      } EOS_RETHROW_EXCEPTIONS(action_type_exception, "Fail to parse action JSON data='${data}'", ("data", data_or_filename));
   }
   return variant_to_bin( account, action, action_args_var );
}

void print_action_tree( const fc::variant& action ) {
   print_action( action );
   const auto& inline_traces = action["inline_traces"].get_array();
   for( const auto& t : inline_traces ) {
      print_action_tree( t );
   }
}

void print_result( const fc::variant& result ) { try {
      if (result.is_object() && result.get_object().contains("processed")) {
         const auto& processed = result["processed"];
         const auto& transaction_id = processed["id"].as_string();
         string status = processed["receipt"].is_object() ? processed["receipt"]["status"].as_string() : "failed";
         int64_t net = -1;
         int64_t cpu = -1;
         if( processed.get_object().contains( "receipt" )) {
            const auto& receipt = processed["receipt"];
            if( receipt.is_object()) {
               net = receipt["net_usage_words"].as_int64() * 8;
               cpu = receipt["cpu_usage_us"].as_int64();
            }
         }

         cerr << status << " transaction: " << transaction_id << "  ";
         if( net < 0 ) {
            cerr << "<unknown>";
         } else {
            cerr << net;
         }
         cerr << " bytes  ";
         if( cpu < 0 ) {
            cerr << "<unknown>";
         } else {
            cerr << cpu;
         }

         cerr << " us\n";

         if( status == "failed" ) {
            auto soft_except = processed["except"].as<optional<fc::exception>>();
            if( soft_except ) {
               edump((soft_except->to_detail_string()));
            }
         } else {
            const auto& actions = processed["action_traces"].get_array();
            for( const auto& a : actions ) {
               print_action_tree( a );
            }
            wlog( "\rwarning: transaction executed locally, but may not be confirmed by the network yet" );
         }
      } else {
         cerr << fc::json::to_pretty_string( result ) << endl;
      }
} FC_CAPTURE_AND_RETHROW( (result) ) }

using std::cout;
void send_actions(std::vector<chain::action>&& actions, int32_t extra_kcpu = 1000, packed_transaction::compression_type compression = packed_transaction::none ) {
   auto result = push_actions( move(actions), extra_kcpu, compression);

   if( tx_print_json ) {
      cout << fc::json::to_pretty_string( result ) << endl;
   } else {
      print_result( result );
   }
}

void send_transaction( signed_transaction& trx, int32_t extra_kcpu, packed_transaction::compression_type compression = packed_transaction::none  ) {
   auto result = push_transaction(trx, extra_kcpu, compression);

   if( tx_print_json ) {
      cout << fc::json::to_pretty_string( result ) << endl;
   } else {
      print_result( result );
   }
}

chain::action create_newaccount(const name& creator, const name& newaccount, public_key_type owner, public_key_type active) {
   return action {
      get_account_permissions(tx_permission, {creator,config::active_name}),
      eosio::chain::newaccount{
         .creator      = creator,
         .name         = newaccount,
         .owner        = eosio::chain::authority{1, {{owner, 1}}, {}},
         .active       = eosio::chain::authority{1, {{active, 1}}, {}}
      }
   };
}

chain::action create_action(const vector<permission_level>& authorization, const account_name& code, const action_name& act, const fc::variant& args) {
   return chain::action{authorization, code, act, variant_to_bin(code, act, args)};
}

chain::action create_buyram(const name& creator, const name& newaccount, const asset& quantity) {
   fc::variant act_payload = fc::mutable_variant_object()
         ("payer", creator.to_string())
         ("receiver", newaccount.to_string())
         ("quant", quantity.to_string());
   return create_action(get_account_permissions(tx_permission, {creator,config::active_name}),
                        config::system_account_name, N(buyram), act_payload);
}

chain::action create_buyrambytes(const name& creator, const name& newaccount, uint32_t numbytes) {
   fc::variant act_payload = fc::mutable_variant_object()
         ("payer", creator.to_string())
         ("receiver", newaccount.to_string())
         ("bytes", numbytes);
   return create_action(get_account_permissions(tx_permission, {creator,config::active_name}),
                        config::system_account_name, N(buyrambytes), act_payload);
}

chain::action create_delegate(const name& from, const name& receiver, const asset& net, const asset& cpu, bool transfer) {
   fc::variant act_payload = fc::mutable_variant_object()
         ("from", from.to_string())
         ("receiver", receiver.to_string())
         ("stake_net_quantity", net.to_string())
         ("stake_cpu_quantity", cpu.to_string())
         ("transfer", transfer);
   return create_action(get_account_permissions(tx_permission, {from,config::active_name}),
                        config::system_account_name, N(delegatebw), act_payload);
}

fc::variant regproducer_variant(const account_name& producer, const public_key_type& key, const string& url, uint16_t location) {
   return fc::mutable_variant_object()
            ("producer", producer)
            ("producer_key", key)
            ("url", url)
            ("location", location)
            ;
}

chain::action create_open(const string& contract, const name& owner, symbol sym, const name& ram_payer) {
   auto open_ = fc::mutable_variant_object
      ("owner", owner)
      ("symbol", sym)
      ("ram_payer", ram_payer);
    return action {
      get_account_permissions(tx_permission, {ram_payer,config::active_name}),
      contract, "open", variant_to_bin( contract, N(open), open_ )
   };
}

chain::action create_transfer(const string& contract, const name& sender, const name& recipient, asset amount, const string& memo ) {

   auto transfer = fc::mutable_variant_object
      ("from", sender)
      ("to", recipient)
      ("quantity", amount)
      ("memo", memo);

   return action {
      get_account_permissions(tx_permission, {sender,config::active_name}),
      contract, "transfer", variant_to_bin( contract, N(transfer), transfer )
   };
}

chain::action create_setabi(const name& account, const bytes& abi) {
   return action {
      get_account_permissions(tx_permission, {account,config::active_name}),
      setabi{
         .account   = account,
         .abi       = abi
      }
   };
}

chain::action create_setcode(const name& account, const bytes& code) {
   return action {
      get_account_permissions(tx_permission, {account,config::active_name}),
      setcode{
         .account   = account,
         .vmtype    = 0,
         .vmversion = 0,
         .code      = code
      }
   };
}

chain::action create_updateauth(const name& account, const name& permission, const name& parent, const authority& auth) {
   return action { get_account_permissions(tx_permission, {account,config::active_name}),
                   updateauth{account, permission, parent, auth}};
}

chain::action create_deleteauth(const name& account, const name& permission) {
   return action { get_account_permissions(tx_permission, {account,config::active_name}),
                   deleteauth{account, permission}};
}

chain::action create_linkauth(const name& account, const name& code, const name& type, const name& requirement) {
   return action { get_account_permissions(tx_permission, {account,config::active_name}),
                   linkauth{account, code, type, requirement}};
}

chain::action create_unlinkauth(const name& account, const name& code, const name& type) {
   return action { get_account_permissions(tx_permission, {account,config::active_name}),
                   unlinkauth{account, code, type}};
}

authority parse_json_authority(const std::string& authorityJsonOrFile) {
   try {
      return json_from_file_or_string(authorityJsonOrFile).as<authority>();
   } EOS_RETHROW_EXCEPTIONS(authority_type_exception, "Fail to parse Authority JSON '${data}'", ("data",authorityJsonOrFile))
}

authority parse_json_authority_or_key(const std::string& authorityJsonOrFile) {
   if (boost::istarts_with(authorityJsonOrFile, "EOS") || boost::istarts_with(authorityJsonOrFile, "PUB_R1")) {
      try {
         return authority(public_key_type(authorityJsonOrFile));
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", authorityJsonOrFile))
   } else {
      auto result = parse_json_authority(authorityJsonOrFile);
      EOS_ASSERT( eosio::chain::validate(result), authority_type_exception, "Authority failed validation! ensure that keys, accounts, and waits are sorted and that the threshold is valid and satisfiable!");
      return result;
   }
}

asset to_asset( account_name code, const string& s ) {
   static map< pair<account_name, eosio::chain::symbol_code>, eosio::chain::symbol> cache;
   auto a = asset::from_string( s );
   eosio::chain::symbol_code sym = a.get_symbol().to_symbol_code();
   auto it = cache.find( make_pair(code, sym) );
   auto sym_str = a.symbol_name();
   if ( it == cache.end() ) {
      auto json = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
                       ("code", code)
                       ("symbol", sym_str)
      );
      auto obj = json.get_object();
      auto obj_it = obj.find( sym_str );
      if (obj_it != obj.end()) {
         auto result = obj_it->value().as<eosio::chain_apis::read_only::get_currency_stats_result>();
         auto p = cache.emplace( make_pair( code, sym ), result.max_supply.get_symbol() );
         it = p.first;
      } else {
         EOS_THROW(symbol_type_exception, "Symbol ${s} is not supported by token contract ${c}", ("s", sym_str)("c", code));
      }
   }
   auto expected_symbol = it->second;
   if ( a.decimals() < expected_symbol.decimals() ) {
      auto factor = expected_symbol.precision() / a.precision();
      auto a_old = a;
      a = asset( a.get_amount() * factor, expected_symbol );
   } else if ( a.decimals() > expected_symbol.decimals() ) {
      EOS_THROW(symbol_type_exception, "Too many decimal digits in ${a}, only ${d} supported", ("a", a)("d", expected_symbol.decimals()));
} //其他精度匹配
   return a;
}

inline asset to_asset( const string& s ) {
   return to_asset( N(eosio.token), s );
}

struct set_account_permission_subcommand {
   name account;
   name permission;
   string authority_json_or_file;
   name parent;
   bool add_code;
   bool remove_code;

   set_account_permission_subcommand(CLI::App* accountCmd) {
      auto permissions = accountCmd->add_subcommand("permission", localized("set parameters dealing with account permissions"));
      permissions->add_option("account", account, localized("The account to set/delete a permission authority for"))->required();
      permissions->add_option("permission", permission, localized("The permission name to set/delete an authority for"))->required();
      permissions->add_option("authority", authority_json_or_file, localized("[delete] NULL, [create/update] public key, JSON string or filename defining the authority, [code] contract name"));
      permissions->add_option("parent", parent, localized("[create] The permission name of this parents permission, defaults to 'active'"));
      permissions->add_flag("--add-code", add_code, localized("[code] add '${code}' permission to specified permission authority", ("code", name(config::eosio_code_name))));
      permissions->add_flag("--remove-code", remove_code, localized("[code] remove '${code}' permission from specified permission authority", ("code", name(config::eosio_code_name))));

      add_standard_transaction_options(permissions, "account@active");

      permissions->set_callback([this] {
         EOSC_ASSERT( !(add_code && remove_code), "ERROR: Either --add-code or --remove-code can be set" );
         EOSC_ASSERT( (add_code ^ remove_code) || !authority_json_or_file.empty(), "ERROR: authority should be specified unless add or remove code permission" );

         authority auth;

         bool need_parent = parent.empty() && (permission != name("owner"));
         bool need_auth = add_code || remove_code;

         if ( !need_auth && boost::iequals(authority_json_or_file, "null") ) {
            send_actions( { create_deleteauth(account, permission) } );
            return;
         }

         if ( need_parent || need_auth ) {
            fc::variant json = call(get_account_func, fc::mutable_variant_object("account_name", account.to_string()));
            auto res = json.as<eosio::chain_apis::read_only::get_account_results>();
            auto itr = std::find_if(res.permissions.begin(), res.permissions.end(), [&](const auto& perm) {
               return perm.perm_name == permission;
            });

            if ( need_parent ) {
//看看我们是否能自动确定合适的父对象
               if ( itr != res.permissions.end() ) {
                  parent = (*itr).parent;
               } else {
//如果这是一个新权限，并且没有父权限，则默认为“活动”
                  parent = name(config::active_name);
               }
            }

            if ( need_auth ) {
               auto actor = (authority_json_or_file.empty()) ? account : name(authority_json_or_file);
               auto code_name = name(config::eosio_code_name);

               if ( itr != res.permissions.end() ) {
//获取现有权限
                  auth = std::move((*itr).required_auth);

                  auto code_perm = permission_level { actor, code_name };
                  auto itr2 = std::lower_bound(auth.accounts.begin(), auth.accounts.end(), code_perm, [&](const auto& perm_level, const auto& value) {
return perm_level.permission < value; //安全，因为有效权限必须按升序排列帐户中的权限
                  });

                  if ( add_code ) {
                     if ( itr2 != auth.accounts.end() && itr2->permission == code_perm ) {
//权限已包含代码权限，提升其权重以满足阈值
                        if ( (*itr2).weight < auth.threshold ) {
                           if ( auth.threshold > std::numeric_limits<weight_type>::max() ) {
                              std::cerr << "ERROR: Threshold is too high to be satisfied by sole code permission" << std::endl;
                              return;
                           }
                           std::cerr << localized("The weight of '${actor}@${code}' in '${permission}' permission authority will be increased up to threshold",
                                                  ("actor", actor)("code", code_name)("permission", permission)) << std::endl;
                           (*itr2).weight = static_cast<weight_type>(auth.threshold);
                        } else {
                           std::cerr << localized("ERROR: The permission '${permission}' already contains '${actor}@${code}'",
                                                  ("permission", permission)("actor", actor)("code", code_name)) << std::endl;
                           return ;
                        }
                     } else {
//向指定权限添加代码权限
                        if ( auth.threshold > std::numeric_limits<weight_type>::max() ) {
                           std::cerr << "ERROR: Threshold is too high to be satisfied by sole code permission" << std::endl;
                           return;
                        }
                        auth.accounts.insert( itr2, permission_level_weight {
                           .permission = { actor, code_name },
                           .weight = static_cast<weight_type>(auth.threshold)
                        });
                     }
                  } else {
                     if ( itr2 != auth.accounts.end() && itr2->permission == code_perm ) {
//删除代码权限，如果删除代码权限后权限变为空，则删除权限
                        auth.accounts.erase( itr2 );
                        if ( auth.keys.empty() && auth.accounts.empty() && auth.waits.empty() ) {
                           send_actions( { create_deleteauth(account, permission) } );
                           return;
                        }
                     } else {
//权限不包含代码权限
                        std::cerr << localized("ERROR: '${actor}@${code}' does not exist in '${permission}' permission authority",
                                               ("actor", actor)("code", code_name)("permission", permission)) << std::endl;
                        return;
                     }
                  }
               } else {
                  if ( add_code ) {
//创建新权限，包括代码权限
                     auth.threshold = 1;
                     auth.accounts.push_back( permission_level_weight {
                        .permission = { actor, code_name },
                        .weight = 1
                     });
                  } else {
//指定的权限不存在，因此无法从中删除代码权限
                     std::cerr << localized("ERROR: The permission '${permission}' does not exist", ("permission", permission)) << std::endl;
                     return;
                  }
               }
            }
         }

         if ( !need_auth ) {
            auth = parse_json_authority_or_key(authority_json_or_file);
         }

         send_actions( { create_updateauth(account, permission, parent, auth) } );
      });
   }
};

struct set_action_permission_subcommand {
   string accountStr;
   string codeStr;
   string typeStr;
   string requirementStr;

   set_action_permission_subcommand(CLI::App* actionRoot) {
      auto permissions = actionRoot->add_subcommand("permission", localized("set parmaters dealing with account permissions"));
      permissions->add_option("account", accountStr, localized("The account to set/delete a permission authority for"))->required();
      permissions->add_option("code", codeStr, localized("The account that owns the code for the action"))->required();
      permissions->add_option("type", typeStr, localized("the type of the action"))->required();
      permissions->add_option("requirement", requirementStr, localized("[delete] NULL, [set/update] The permission name require for executing the given action"))->required();

      add_standard_transaction_options(permissions, "account@active");

      permissions->set_callback([this] {
         name account = name(accountStr);
         name code = name(codeStr);
         name type = name(typeStr);
         bool is_delete = boost::iequals(requirementStr, "null");

         if (is_delete) {
            send_actions({create_unlinkauth(account, code, type)});
         } else {
            name requirement = name(requirementStr);
            send_actions({create_linkauth(account, code, type, requirement)});
         }
      });
   }
};


bool local_port_used() {
    using namespace boost::asio;

    io_service ios;
local::stream_protocol::endpoint endpoint(wallet_url.substr(strlen("unix://“）
    local::stream_protocol::socket socket(ios);
    boost::system::error_code ec;
    socket.connect(endpoint, ec);

    return !ec;
}

void try_local_port(uint32_t duration) {
   using namespace std::chrono;
   auto start_time = duration_cast<std::chrono::milliseconds>( system_clock::now().time_since_epoch() ).count();
   while ( !local_port_used()) {
      if (duration_cast<std::chrono::milliseconds>( system_clock::now().time_since_epoch()).count() - start_time > duration ) {
         std::cerr << "Unable to connect to keosd, if keosd is running please kill the process and try again.\n";
         throw connection_exception(fc::log_messages{FC_LOG_MESSAGE(error, "Unable to connect to keosd")});
      }
   }
}

void ensure_keosd_running(CLI::App* app) {
    if (no_auto_keosd)
        return;
//get，version，net不需要keosd
    if (tx_skip_sign || app->got_subcommand("get") || app->got_subcommand("version") || app->got_subcommand("net"))
        return;
if (app->get_subcommand("create")->got_subcommand("key")) //创建密钥不需要钱包
       return;
    if (auto* subapp = app->get_subcommand("system")) {
if (subapp->got_subcommand("listproducers") || subapp->got_subcommand("listbw") || subapp->got_subcommand("bidnameinfo")) //系统列表*不需要钱包
         return;
    }
    if (wallet_url != default_wallet_url)
      return;

    if (local_port_used())
       return;

    boost::filesystem::path binPath = boost::dll::program_location();
    binPath.remove_filename();
//这种额外的检查是必要的，当运行这样的谱号：/谱号…
    if (binPath.filename_is_dot())
        binPath.remove_filename();
binPath.append(key_store_executable_name); //如果Cleos和Keosd在同一安装目录中
    if (!boost::filesystem::exists(binPath)) {
        binPath.remove_filename().remove_filename().append("keosd").append(key_store_executable_name);
    }

    if (boost::filesystem::exists(binPath)) {
        namespace bp = boost::process;
        binPath = boost::filesystem::canonical(binPath);

        vector<std::string> pargs;
        pargs.push_back("--http-server-address");
        pargs.push_back("");
        pargs.push_back("--https-server-address");
        pargs.push_back("");
        pargs.push_back("--unix-socket-path");
        pargs.push_back(string(key_store_executable_name) + ".sock");

        ::boost::process::child keos(binPath, pargs,
                                     bp::std_in.close(),
                                     bp::std_out > bp::null,
                                     bp::std_err > bp::null);
        if (keos.running()) {
            std::cerr << binPath << " launched" << std::endl;
            keos.detach();
            try_local_port(2000);
        } else {
            std::cerr << "No wallet service listening on " << wallet_url << ". Failed to launch " << binPath << std::endl;
        }
    } else {
        std::cerr << "No wallet service listening on "
                  << ". Cannot automatically start keosd because keosd was not found." << std::endl;
    }
}


CLI::callback_t obsoleted_option_host_port = [](CLI::results_t) {
   std::cerr << localized("Host and port options (-H, --wallet-host, etc.) have been replaced with -u/--url and --wallet-url\n"
"Use for example -u http://localhost:8888或--url https://example.invalid/\n”）；
   exit(1);
   return false;
};

struct register_producer_subcommand {
   string producer_str;
   string producer_key_str;
   string url;
   uint16_t loc = 0;

   register_producer_subcommand(CLI::App* actionRoot) {
      auto register_producer = actionRoot->add_subcommand("regproducer", localized("Register a new producer"));
      register_producer->add_option("account", producer_str, localized("The account to register as a producer"))->required();
      register_producer->add_option("producer_key", producer_key_str, localized("The producer's public key"))->required();
      register_producer->add_option("url", url, localized("url where info about producer can be found"), true);
      register_producer->add_option("location", loc, localized("relative location for purpose of nearest neighbor scheduling"), true);
      add_standard_transaction_options(register_producer, "account@active");


      register_producer->set_callback([this] {
         public_key_type producer_key;
         try {
            producer_key = public_key_type(producer_key_str);
         } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid producer public key: ${public_key}", ("public_key", producer_key_str))

         auto regprod_var = regproducer_variant(producer_str, producer_key, url, loc );
         auto accountPermissions = get_account_permissions(tx_permission, {producer_str,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(regproducer), regprod_var)});
      });
   }
};

struct create_account_subcommand {
   string creator;
   string account_name;
   string owner_key_str;
   string active_key_str;
   string stake_net;
   string stake_cpu;
   uint32_t buy_ram_bytes_in_kbytes = 0;
   uint32_t buy_ram_bytes = 0;
   string buy_ram_eos;
   bool transfer;
   bool simple;

   create_account_subcommand(CLI::App* actionRoot, bool s) : simple(s) {
      auto createAccount = actionRoot->add_subcommand(
                              (simple ? "account" : "newaccount"),
                              (simple ? localized("Create a new account on the blockchain (assumes system contract does not restrict RAM usage)")
                                      : localized("Create a new account on the blockchain with initial resources") )
      );
      createAccount->add_option("creator", creator, localized("The name of the account creating the new account"))->required();
      createAccount->add_option("name", account_name, localized("The name of the new account"))->required();
      createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key for the new account"))->required();
      createAccount->add_option("ActiveKey", active_key_str, localized("The active public key for the new account"));

      if (!simple) {
         createAccount->add_option("--stake-net", stake_net,
                                   (localized("The amount of tokens delegated for net bandwidth")))->required();
         createAccount->add_option("--stake-cpu", stake_cpu,
                                   (localized("The amount of tokens delegated for CPU bandwidth")))->required();
         createAccount->add_option("--buy-ram-kbytes", buy_ram_bytes_in_kbytes,
                                   (localized("The amount of RAM bytes to purchase for the new account in kibibytes (KiB)")));
         createAccount->add_option("--buy-ram-bytes", buy_ram_bytes,
                                   (localized("The amount of RAM bytes to purchase for the new account in bytes")));
         createAccount->add_option("--buy-ram", buy_ram_eos,
                                   (localized("The amount of RAM bytes to purchase for the new account in tokens")));
         createAccount->add_flag("--transfer", transfer,
                                 (localized("Transfer voting power and right to unstake tokens to receiver")));
      }

      add_standard_transaction_options(createAccount, "creator@active");

      createAccount->set_callback([this] {
            if( !active_key_str.size() )
               active_key_str = owner_key_str;
            public_key_type owner_key, active_key;
            try {
               owner_key = public_key_type(owner_key_str);
            } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str));
            try {
               active_key = public_key_type(active_key_str);
            } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str));
            auto create = create_newaccount(creator, account_name, owner_key, active_key);
            if (!simple) {
               EOSC_ASSERT( buy_ram_eos.size() || buy_ram_bytes_in_kbytes || buy_ram_bytes, "ERROR: One of --buy-ram, --buy-ram-kbytes or --buy-ram-bytes should have non-zero value" );
               EOSC_ASSERT( !buy_ram_bytes_in_kbytes || !buy_ram_bytes, "ERROR: --buy-ram-kbytes and --buy-ram-bytes cannot be set at the same time" );
               action buyram = !buy_ram_eos.empty() ? create_buyram(creator, account_name, to_asset(buy_ram_eos))
                  : create_buyrambytes(creator, account_name, (buy_ram_bytes_in_kbytes) ? (buy_ram_bytes_in_kbytes * 1024) : buy_ram_bytes);
               auto net = to_asset(stake_net);
               auto cpu = to_asset(stake_cpu);
               if ( net.get_amount() != 0 || cpu.get_amount() != 0 ) {
                  action delegate = create_delegate( creator, account_name, net, cpu, transfer);
                  send_actions( { create, buyram, delegate } );
               } else {
                  send_actions( { create, buyram } );
               }
            } else {
               send_actions( { create } );
            }
      });
   }
};

struct unregister_producer_subcommand {
   string producer_str;

   unregister_producer_subcommand(CLI::App* actionRoot) {
      auto unregister_producer = actionRoot->add_subcommand("unregprod", localized("Unregister an existing producer"));
      unregister_producer->add_option("account", producer_str, localized("The account to unregister as a producer"))->required();
      add_standard_transaction_options(unregister_producer, "account@active");

      unregister_producer->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("producer", producer_str);

         auto accountPermissions = get_account_permissions(tx_permission, {producer_str,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(unregprod), act_payload)});
      });
   }
};

struct vote_producer_proxy_subcommand {
   string voter_str;
   string proxy_str;

   vote_producer_proxy_subcommand(CLI::App* actionRoot) {
      auto vote_proxy = actionRoot->add_subcommand("proxy", localized("Vote your stake through a proxy"));
      vote_proxy->add_option("voter", voter_str, localized("The voting account"))->required();
      vote_proxy->add_option("proxy", proxy_str, localized("The proxy account"))->required();
      add_standard_transaction_options(vote_proxy, "voter@active");

      vote_proxy->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("voter", voter_str)
                  ("proxy", proxy_str)
                  ("producers", std::vector<account_name>{});
         auto accountPermissions = get_account_permissions(tx_permission, {voter_str,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

struct vote_producers_subcommand {
   string voter_str;
   vector<eosio::name> producer_names;

   vote_producers_subcommand(CLI::App* actionRoot) {
      auto vote_producers = actionRoot->add_subcommand("prods", localized("Vote for one or more producers"));
      vote_producers->add_option("voter", voter_str, localized("The voting account"))->required();
      vote_producers->add_option("producers", producer_names, localized("The account(s) to vote for. All options from this position and following will be treated as the producer list."))->required();
      add_standard_transaction_options(vote_producers, "voter@active");

      vote_producers->set_callback([this] {

         std::sort( producer_names.begin(), producer_names.end() );

         fc::variant act_payload = fc::mutable_variant_object()
                  ("voter", voter_str)
                  ("proxy", "")
                  ("producers", producer_names);
         auto accountPermissions = get_account_permissions(tx_permission, {voter_str,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

struct approve_producer_subcommand {
   eosio::name voter;
   eosio::name producer_name;

   approve_producer_subcommand(CLI::App* actionRoot) {
      auto approve_producer = actionRoot->add_subcommand("approve", localized("Add one producer to list of voted producers"));
      approve_producer->add_option("voter", voter, localized("The voting account"))->required();
      approve_producer->add_option("producer", producer_name, localized("The account to vote for"))->required();
      add_standard_transaction_options(approve_producer, "voter@active");

      approve_producer->set_callback([this] {
            auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", name(config::system_account_name).to_string())
                               ("scope", name(config::system_account_name).to_string())
                               ("table", "voters")
                               ("table_key", "owner")
                               ("lower_bound", voter.value)
                               ("upper_bound", voter.value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//当Cleos不再需要支持早于1.5.0的nodeos版本时，更改为voter.value
                               ("limit", 1)
            );
            auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
//当cleos不再需要支持早于1.5.0的nodeos版本时，下面if语句中的条件可以简单地为res.rows.empty（）。
//尽管此子命令实际上会更改投票人的投票，但最好保留此检查以保护
//针对未来可能出现的链插件错误。
            if( res.rows.empty() || res.rows[0].get_object()["owner"].as_string() != name(voter).to_string() ) {
               std::cerr << "Voter info not found for account " << voter << std::endl;
               return;
            }
            EOS_ASSERT( 1 == res.rows.size(), multiple_voter_info, "More than one voter_info for account" );
            auto prod_vars = res.rows[0]["producers"].get_array();
            vector<eosio::name> prods;
            for ( auto& x : prod_vars ) {
               prods.push_back( name(x.as_string()) );
            }
            prods.push_back( producer_name );
            std::sort( prods.begin(), prods.end() );
            auto it = std::unique( prods.begin(), prods.end() );
            if (it != prods.end() ) {
               std::cerr << "Producer \"" << producer_name << "\" is already on the list." << std::endl;
               return;
            }
            fc::variant act_payload = fc::mutable_variant_object()
               ("voter", voter)
               ("proxy", "")
               ("producers", prods);
            auto accountPermissions = get_account_permissions(tx_permission, {voter,config::active_name});
            send_actions({create_action(accountPermissions, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

struct unapprove_producer_subcommand {
   eosio::name voter;
   eosio::name producer_name;

   unapprove_producer_subcommand(CLI::App* actionRoot) {
      auto approve_producer = actionRoot->add_subcommand("unapprove", localized("Remove one producer from list of voted producers"));
      approve_producer->add_option("voter", voter, localized("The voting account"))->required();
      approve_producer->add_option("producer", producer_name, localized("The account to remove from voted producers"))->required();
      add_standard_transaction_options(approve_producer, "voter@active");

      approve_producer->set_callback([this] {
            auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", name(config::system_account_name).to_string())
                               ("scope", name(config::system_account_name).to_string())
                               ("table", "voters")
                               ("table_key", "owner")
                               ("lower_bound", voter.value)
                               ("upper_bound", voter.value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//当Cleos不再需要支持早于1.5.0的nodeos版本时，更改为voter.value
                               ("limit", 1)
            );
            auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
//当cleos不再需要支持早于1.5.0的nodeos版本时，下面if语句中的条件可以简单地为res.rows.empty（）。
//尽管此子命令实际上会更改投票人的投票，但最好保留此检查以保护
//针对未来可能出现的链插件错误。
            if( res.rows.empty() || res.rows[0].get_object()["owner"].as_string() != name(voter).to_string() ) {
               std::cerr << "Voter info not found for account " << voter << std::endl;
               return;
            }
            EOS_ASSERT( 1 == res.rows.size(), multiple_voter_info, "More than one voter_info for account" );
            auto prod_vars = res.rows[0]["producers"].get_array();
            vector<eosio::name> prods;
            for ( auto& x : prod_vars ) {
               prods.push_back( name(x.as_string()) );
            }
            auto it = std::remove( prods.begin(), prods.end(), producer_name );
            if (it == prods.end() ) {
               std::cerr << "Cannot remove: producer \"" << producer_name << "\" is not on the list." << std::endl;
               return;
            }
prods.erase( it, prods.end() ); //应始终只删除一个元素
            fc::variant act_payload = fc::mutable_variant_object()
               ("voter", voter)
               ("proxy", "")
               ("producers", prods);
            auto accountPermissions = get_account_permissions(tx_permission, {voter,config::active_name});
            send_actions({create_action(accountPermissions, config::system_account_name, N(voteproducer), act_payload)});
      });
   }
};

struct list_producers_subcommand {
   bool print_json = false;
   uint32_t limit = 50;
   std::string lower;

   list_producers_subcommand(CLI::App* actionRoot) {
      auto list_producers = actionRoot->add_subcommand("listproducers", localized("List producers"));
      list_producers->add_flag("--json,-j", print_json, localized("Output in JSON format"));
      list_producers->add_option("-l,--limit", limit, localized("The maximum number of rows to return"));
      list_producers->add_option("-L,--lower", lower, localized("lower bound value of key, defaults to first"));
      list_producers->set_callback([this] {
         auto rawResult = call(get_producers_func, fc::mutable_variant_object
            ("json", true)("lower_bound", lower)("limit", limit));
         if ( print_json ) {
            std::cout << fc::json::to_pretty_string(rawResult) << std::endl;
            return;
         }
         auto result = rawResult.as<eosio::chain_apis::read_only::get_producers_result>();
         if ( result.rows.empty() ) {
            std::cout << "No producers found" << std::endl;
            return;
         }
         auto weight = result.total_producer_vote_weight;
         if ( !weight )
            weight = 1;
         printf("%-13s %-57s %-59s %s\n", "Producer", "Producer key", "Url", "Scaled votes");
         for ( auto& row : result.rows )
            printf("%-13.13s %-57.57s %-59.59s %1.4f\n",
                   row["owner"].as_string().c_str(),
                   row["producer_key"].as_string().c_str(),
                   row["url"].as_string().c_str(),
                   row["total_votes"].as_double() / weight);
         if ( !result.more.empty() )
            std::cout << "-L " << result.more << " for more" << std::endl;
      });
   }
};

struct get_schedule_subcommand {
   bool print_json = false;

   void print(const char* name, const fc::variant& schedule) {
      if (schedule.is_null()) {
         printf("%s schedule empty\n\n", name);
         return;
      }
      printf("%s schedule version %s\n", name, schedule["version"].as_string().c_str());
      printf("    %-13s %s\n", "Producer", "Producer key");
      printf("    %-13s %s\n", "=============", "==================");
      for (auto& row: schedule["producers"].get_array())
         printf("    %-13s %s\n", row["producer_name"].as_string().c_str(), row["block_signing_key"].as_string().c_str());
      printf("\n");
   }

   get_schedule_subcommand(CLI::App* actionRoot) {
      auto get_schedule = actionRoot->add_subcommand("schedule", localized("Retrieve the producer schedule"));
      get_schedule->add_flag("--json,-j", print_json, localized("Output in JSON format"));
      get_schedule->set_callback([this] {
         auto result = call(get_schedule_func, fc::mutable_variant_object());
         if ( print_json ) {
            std::cout << fc::json::to_pretty_string(result) << std::endl;
            return;
         }
         print("active", result["active"]);
         print("pending", result["pending"]);
         print("proposed", result["proposed"]);
      });
   }
};

struct get_transaction_id_subcommand {
   string trx_to_check;

   get_transaction_id_subcommand(CLI::App* actionRoot) {
      auto get_transaction_id = actionRoot->add_subcommand("transaction_id", localized("Get transaction id given transaction object"));
      get_transaction_id->add_option("transaction", trx_to_check, localized("The JSON string or filename defining the transaction which transaction id we want to retrieve"))->required();

      get_transaction_id->set_callback([&] {
         try {
            auto trx_var = json_from_file_or_string(trx_to_check);
            auto trx = trx_var.as<transaction>();
            std::cout << string(trx.id()) << std::endl;
         } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_check))
      });
   }
};

struct delegate_bandwidth_subcommand {
   string from_str;
   string receiver_str;
   string stake_net_amount;
   string stake_cpu_amount;
   string stake_storage_amount;
   string buy_ram_amount;
   uint32_t buy_ram_bytes = 0;
   bool transfer = false;

   delegate_bandwidth_subcommand(CLI::App* actionRoot) {
      auto delegate_bandwidth = actionRoot->add_subcommand("delegatebw", localized("Delegate bandwidth"));
      delegate_bandwidth->add_option("from", from_str, localized("The account to delegate bandwidth from"))->required();
      delegate_bandwidth->add_option("receiver", receiver_str, localized("The account to receive the delegated bandwidth"))->required();
      delegate_bandwidth->add_option("stake_net_quantity", stake_net_amount, localized("The amount of tokens to stake for network bandwidth"))->required();
      delegate_bandwidth->add_option("stake_cpu_quantity", stake_cpu_amount, localized("The amount of tokens to stake for CPU bandwidth"))->required();
      delegate_bandwidth->add_option("--buyram", buy_ram_amount, localized("The amount of tokens to buyram"));
      delegate_bandwidth->add_option("--buy-ram-bytes", buy_ram_bytes, localized("The amount of RAM to buy in number of bytes"));
      delegate_bandwidth->add_flag("--transfer", transfer, localized("Transfer voting power and right to unstake tokens to receiver"));
      add_standard_transaction_options(delegate_bandwidth, "from@active");

      delegate_bandwidth->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("from", from_str)
                  ("receiver", receiver_str)
                  ("stake_net_quantity", to_asset(stake_net_amount))
                  ("stake_cpu_quantity", to_asset(stake_cpu_amount))
                  ("transfer", transfer);
         auto accountPermissions = get_account_permissions(tx_permission, {from_str,config::active_name});
         std::vector<chain::action> acts{create_action(accountPermissions, config::system_account_name, N(delegatebw), act_payload)};
         EOSC_ASSERT( !(buy_ram_amount.size()) || !buy_ram_bytes, "ERROR: --buyram and --buy-ram-bytes cannot be set at the same time" );
         if (buy_ram_amount.size()) {
            acts.push_back( create_buyram(from_str, receiver_str, to_asset(buy_ram_amount)) );
         } else if (buy_ram_bytes) {
            acts.push_back( create_buyrambytes(from_str, receiver_str, buy_ram_bytes) );
         }
         send_actions(std::move(acts));
      });
   }
};

struct undelegate_bandwidth_subcommand {
   string from_str;
   string receiver_str;
   string unstake_net_amount;
   string unstake_cpu_amount;
   uint64_t unstake_storage_bytes;

   undelegate_bandwidth_subcommand(CLI::App* actionRoot) {
      auto undelegate_bandwidth = actionRoot->add_subcommand("undelegatebw", localized("Undelegate bandwidth"));
      undelegate_bandwidth->add_option("from", from_str, localized("The account undelegating bandwidth"))->required();
      undelegate_bandwidth->add_option("receiver", receiver_str, localized("The account to undelegate bandwidth from"))->required();
      undelegate_bandwidth->add_option("unstake_net_quantity", unstake_net_amount, localized("The amount of tokens to undelegate for network bandwidth"))->required();
      undelegate_bandwidth->add_option("unstake_cpu_quantity", unstake_cpu_amount, localized("The amount of tokens to undelegate for CPU bandwidth"))->required();
      add_standard_transaction_options(undelegate_bandwidth, "from@active");

      undelegate_bandwidth->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("from", from_str)
                  ("receiver", receiver_str)
                  ("unstake_net_quantity", to_asset(unstake_net_amount))
                  ("unstake_cpu_quantity", to_asset(unstake_cpu_amount));
         auto accountPermissions = get_account_permissions(tx_permission, {from_str,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(undelegatebw), act_payload)});
      });
   }
};

struct bidname_subcommand {
   string bidder_str;
   string newname_str;
   string bid_amount;
   bidname_subcommand(CLI::App *actionRoot) {
      auto bidname = actionRoot->add_subcommand("bidname", localized("Name bidding"));
      bidname->add_option("bidder", bidder_str, localized("The bidding account"))->required();
      bidname->add_option("newname", newname_str, localized("The bidding name"))->required();
      bidname->add_option("bid", bid_amount, localized("The amount of tokens to bid"))->required();
      add_standard_transaction_options(bidname, "bidder@active");
      bidname->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("bidder", bidder_str)
                  ("newname", newname_str)
                  ("bid", to_asset(bid_amount));
         auto accountPermissions = get_account_permissions(tx_permission, {bidder_str,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(bidname), act_payload)});
      });
   }
};

struct bidname_info_subcommand {
   bool print_json = false;
   name newname;
   bidname_info_subcommand(CLI::App* actionRoot) {
      auto list_producers = actionRoot->add_subcommand("bidnameinfo", localized("Get bidname info"));
      list_producers->add_flag("--json,-j", print_json, localized("Output in JSON format"));
      list_producers->add_option("newname", newname, localized("The bidding name"))->required();
      list_producers->set_callback([this] {
         auto rawResult = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", "eosio")("scope", "eosio")("table", "namebids")
                               ("lower_bound", newname.value)
                               ("upper_bound", newname.value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//当cleos不再需要支持早于1.5.0的nodeos版本时，改为newname.value。
                               ("limit", 1));
         if ( print_json ) {
            std::cout << fc::json::to_pretty_string(rawResult) << std::endl;
            return;
         }
         auto result = rawResult.as<eosio::chain_apis::read_only::get_table_rows_result>();
//当cleos不再需要支持早于1.5.0的nodeos版本时，下面if语句中的条件可以简单地为res.rows.empty（）。
         if( result.rows.empty() || result.rows[0].get_object()["newname"].as_string() != newname.to_string() ) {
            std::cout << "No bidname record found" << std::endl;
            return;
         }
         const auto& row = result.rows[0];
         string time = row["last_bid_time"].as_string();
         try {
             time = (string)fc::time_point(fc::microseconds(to_uint64(time)));
         } catch (fc::parse_error_exception&) {
         }
         int64_t bid = row["high_bid"].as_int64();
         std::cout << std::left << std::setw(18) << "bidname:" << std::right << std::setw(24) << row["newname"].as_string() << "\n"
                   << std::left << std::setw(18) << "highest bidder:" << std::right << std::setw(24) << row["high_bidder"].as_string() << "\n"
                   << std::left << std::setw(18) << "highest bid:" << std::right << std::setw(24) << (bid > 0 ? bid : -bid) << "\n"
                   << std::left << std::setw(18) << "last bid time:" << std::right << std::setw(24) << time << std::endl;
         if (bid < 0) std::cout << "This auction has already closed" << std::endl;
      });
   }
};

struct list_bw_subcommand {
   eosio::name account;
   bool print_json = false;

   list_bw_subcommand(CLI::App* actionRoot) {
      auto list_bw = actionRoot->add_subcommand("listbw", localized("List delegated bandwidth"));
      list_bw->add_option("account", account, localized("The account delegated bandwidth"))->required();
      list_bw->add_flag("--json,-j", print_json, localized("Output in JSON format") );

      list_bw->set_callback([this] {
//获取用户帐户范围内的整个表
            auto result = call(get_table_func, fc::mutable_variant_object("json", true)
                               ("code", name(config::system_account_name).to_string())
                               ("scope", account.to_string())
                               ("table", "delband")
            );
            if (!print_json) {
               auto res = result.as<eosio::chain_apis::read_only::get_table_rows_result>();
               if ( !res.rows.empty() ) {
                  std::cout << std::setw(13) << std::left << "Receiver" << std::setw(21) << std::left << "Net bandwidth"
                            << std::setw(21) << std::left << "CPU bandwidth" << std::endl;
                  for ( auto& r : res.rows ){
                     std::cout << std::setw(13) << std::left << r["to"].as_string()
                               << std::setw(21) << std::left << r["net_weight"].as_string()
                               << std::setw(21) << std::left << r["cpu_weight"].as_string()
                               << std::endl;
                  }
               } else {
                  std::cerr << "Delegated bandwidth not found" << std::endl;
               }
            } else {
               std::cout << fc::json::to_pretty_string(result) << std::endl;
            }
      });
   }
};

struct buyram_subcommand {
   string from_str;
   string receiver_str;
   string amount;
   bool kbytes = false;
   bool bytes = false;

   buyram_subcommand(CLI::App* actionRoot) {
      auto buyram = actionRoot->add_subcommand("buyram", localized("Buy RAM"));
      buyram->add_option("payer", from_str, localized("The account paying for RAM"))->required();
      buyram->add_option("receiver", receiver_str, localized("The account receiving bought RAM"))->required();
      buyram->add_option("amount", amount, localized("The amount of tokens to pay for RAM, or number of bytes/kibibytes of RAM if --bytes/--kbytes is set"))->required();
      buyram->add_flag("--kbytes,-k", kbytes, localized("buyram in number of kibibytes (KiB)"));
      buyram->add_flag("--bytes,-b", bytes, localized("buyram in number of bytes"));
      add_standard_transaction_options(buyram, "payer@active");
      buyram->set_callback([this] {
         EOSC_ASSERT( !kbytes || !bytes, "ERROR: --kbytes and --bytes cannot be set at the same time" );
         if (kbytes || bytes) {
            send_actions( { create_buyrambytes(from_str, receiver_str, fc::to_uint64(amount) * ((kbytes) ? 1024ull : 1ull)) } );
         } else {
            send_actions( { create_buyram(from_str, receiver_str, to_asset(amount)) } );
         }
      });
   }
};

struct sellram_subcommand {
   string from_str;
   string receiver_str;
   uint64_t amount;

   sellram_subcommand(CLI::App* actionRoot) {
      auto sellram = actionRoot->add_subcommand("sellram", localized("Sell RAM"));
      sellram->add_option("account", receiver_str, localized("The account to receive tokens for sold RAM"))->required();
      sellram->add_option("bytes", amount, localized("Number of RAM bytes to sell"))->required();
      add_standard_transaction_options(sellram, "account@active");

      sellram->set_callback([this] {
            fc::variant act_payload = fc::mutable_variant_object()
               ("account", receiver_str)
               ("bytes", amount);
            auto accountPermissions = get_account_permissions(tx_permission, {receiver_str,config::active_name});
            send_actions({create_action(accountPermissions, config::system_account_name, N(sellram), act_payload)});
         });
   }
};

struct claimrewards_subcommand {
   string owner;

   claimrewards_subcommand(CLI::App* actionRoot) {
      auto claim_rewards = actionRoot->add_subcommand("claimrewards", localized("Claim producer rewards"));
      claim_rewards->add_option("owner", owner, localized("The account to claim rewards for"))->required();
      add_standard_transaction_options(claim_rewards, "owner@active");

      claim_rewards->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("owner", owner);
         auto accountPermissions = get_account_permissions(tx_permission, {owner,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(claimrewards), act_payload)});
      });
   }
};

struct regproxy_subcommand {
   string proxy;

   regproxy_subcommand(CLI::App* actionRoot) {
      auto register_proxy = actionRoot->add_subcommand("regproxy", localized("Register an account as a proxy (for voting)"));
      register_proxy->add_option("proxy", proxy, localized("The proxy account to register"))->required();
      add_standard_transaction_options(register_proxy, "proxy@active");

      register_proxy->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("proxy", proxy)
                  ("isproxy", true);
         auto accountPermissions = get_account_permissions(tx_permission, {proxy,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(regproxy), act_payload)});
      });
   }
};

struct unregproxy_subcommand {
   string proxy;

   unregproxy_subcommand(CLI::App* actionRoot) {
      auto unregister_proxy = actionRoot->add_subcommand("unregproxy", localized("Unregister an account as a proxy (for voting)"));
      unregister_proxy->add_option("proxy", proxy, localized("The proxy account to unregister"))->required();
      add_standard_transaction_options(unregister_proxy, "proxy@active");

      unregister_proxy->set_callback([this] {
         fc::variant act_payload = fc::mutable_variant_object()
                  ("proxy", proxy)
                  ("isproxy", false);
         auto accountPermissions = get_account_permissions(tx_permission, {proxy,config::active_name});
         send_actions({create_action(accountPermissions, config::system_account_name, N(regproxy), act_payload)});
      });
   }
};

struct canceldelay_subcommand {
   string canceling_account;
   string canceling_permission;
   string trx_id;

   canceldelay_subcommand(CLI::App* actionRoot) {
      auto cancel_delay = actionRoot->add_subcommand("canceldelay", localized("Cancel a delayed transaction"));
      cancel_delay->add_option("canceling_account", canceling_account, localized("Account from authorization on the original delayed transaction"))->required();
      cancel_delay->add_option("canceling_permission", canceling_permission, localized("Permission from authorization on the original delayed transaction"))->required();
      cancel_delay->add_option("trx_id", trx_id, localized("The transaction id of the original delayed transaction"))->required();
      add_standard_transaction_options(cancel_delay, "canceling_account@canceling_permission");

      cancel_delay->set_callback([this] {
         auto canceling_auth = permission_level{canceling_account, canceling_permission};
         fc::variant act_payload = fc::mutable_variant_object()
                  ("canceling_auth", canceling_auth)
                  ("trx_id", trx_id);
         auto accountPermissions = get_account_permissions(tx_permission, canceling_auth);
         send_actions({create_action(accountPermissions, config::system_account_name, N(canceldelay), act_payload)});
      });
   }
};

void get_account( const string& accountName, const string& coresym, bool json_format ) {
   fc::variant json;
   if (coresym.empty()) {
      json = call(get_account_func, fc::mutable_variant_object("account_name", accountName));
   }
   else {
      json = call(get_account_func, fc::mutable_variant_object("account_name", accountName)("expected_core_symbol", symbol::from_string(coresym)));
   }

   auto res = json.as<eosio::chain_apis::read_only::get_account_results>();
   if (!json_format) {
      asset staked;
      asset unstaking;

      if( res.core_liquid_balance.valid() ) {
unstaking = asset( 0, res.core_liquid_balance->get_symbol() ); //纠正不稳定资产的核心符号。
staked = asset( 0, res.core_liquid_balance->get_symbol() );    //标记资产的正确核心符号。
      }

      std::cout << "created: " << string(res.created) << std::endl;

      if(res.privileged) std::cout << "privileged: true" << std::endl;

      constexpr size_t indent_size = 5;
      const string indent(indent_size, ' ');

      std::cout << "permissions: " << std::endl;
      /*rdered_map<name，vector<name>/*children*/>树；
      vector<name>roots；//我们没有多个根，但是我们可以在这里轻松地处理它们，所以我们来做一下以防万一
      无序的_map<name，eosio:：chain_apis:：permission>cache；
      for（auto&perm:res.permissions）
         如果（perm.parent）
            树[perm.parent].向后推（perm.perm\u name）；
         }否则{
            根。推回（perm.perm-name）；
         }
         auto name=perm.perm_name；//移动'perm'前保留副本，因为可以首先评估emplace的drunce参数
         //看起来有点疯狂，但应该很有效率
         cache.insert（std:：make_pair（name，std:：move（perm）））；
      }
      std:：function<void（account_name，int）>df_print=[&]（account_name，int depth）->void
         auto&p=cache.at（名称）；
         std：：cout<<indent<<std:：string（depth*3，”）<<name<<“<<std:：setw（5）<<p.required_auth.threshold<<“：”；
         常量char*sep=“”；
         对于（auto it=p.required_auth.keys.begin（）；it！=P.Required_auth.keys.end（）；++it）
            std：：cout<<sep<<it->weight<“<<string（it->key）；
            SEP＝“，”；
         }
         for（自动和帐户：P.Required_auth.accounts）
            std：：cout<<sep<<acc.weight<“<<string（acc.permission.actor）<<'@'<<string（acc.permission.permission）；
            SEP＝“，”；
         }
         std:：cout<<std:：endl；
         auto it=tree.find（名称）；
         如果（它）！=树（）（{）
            auto&children=it->second；
            排序（children.begin（），children.end（））；
            用于（auto&n:children）
               //我们有一个树，而不是一个图，所以不需要检查已经访问的节点
               DFS打印（n，深度+1）；
            }
         //否则是叶节点
      }；
      std：：sort（roots.begin（），roots.end（））；
      对于（auto r:roots）
         DFS打印（R，0）；
      }

      auto to_pretty_net=[]（int64_t nbytes，uint8_t width_for_units=5）
         如果（nbytes=-1）
             //特殊情况。把它当作无限的
             返回std:：string（“unlimited”）；
         }

         string unit=“字节”；
         double bytes=静态\u cast<double>（nbytes）；
         if（字节数>=1024*1024*1024*1024ll）
             单位=“提包”；
             字节数/=1024*1024*1024*1024ll；
         else if（bytes>=1024*1024*1024）
             单位=“GIB”；
             字节数/=1024*1024*1024；
         else if（bytes>=1024*1024）
             单位=“MIB”；
             字节数/=1024*1024；
         else if（bytes>=1024）
             单位=“KIB”；
             字节/＝1024；
         }
         std：：字符串流ss；
         ss<<设定精度（4）；
         ss<<bytes<<“”；
         if（宽度\单位>0）
            ss<<std:：left<<setw（宽度_表示_单位）；
         SS <单位；
         返回ss.str（）；
      }；



      std:：cout<“内存：”<<std:：endl
                <<indent<“quota：”<<std:：setw（15）<<to撊pretty_net（res.ram_quota）<“used：”<<std:：setw（15）<<to撊pretty_net（res.ram_usage）<<std:：endl<<std:：endl；

      std:：cout<“净带宽：”<<std:：endl；
      if（res.total_resources.is_object（））
         auto net_total=to_asset（res.total_resources.get_object（）[“net_weight”].as_string（））；

         如果（net_total.get_symbol（）！=unstaking.get_symbol（））
            //响应请求的节点的核心符号不同于内置在cleos中的核心符号
            unstaking=资产（0，net_total.get_symbol（））；//正确的非staking资产的核心符号。
            staked=asset（0，net_total.get_symbol（））；//为staked asset更正核心符号。
         }

         if（res.self_delegated_bandwidth.is_object（））
            asset net_own=asset:：from_string（res.self_delegated_bandwidth.get_object（）[“net_weight”].as_string（））；
            标桩=净所有权；

            auto net_others=net_total-net_own；

            std:：cout<<indent<<staked:“<<std:：setw（20）<<net_own
                      <<std:：string（11，“）<<”（从账户委托给自身的总股份）“<<std:：endl
                      <<indent<“委派：”<<std:：setw（17）<<net_others
                      <<std:：string（11，“）<<”（total staked delegated to account from others）”<<std:：endl；
         }
         否则{
            auto net_others=net_total；
            std：：cout<<indent<<委托：“<<std：：setw（17）<<net_others
                      <<std:：string（11，“）<<”（total staked delegated to account from others）”<<std:：endl；
         }
      }


      auto to_pretty_time=[]（int64_t nmicro，uint8_t width_for_units=5）
         如果（nmicro=-1）
             //特殊情况。把它当作无限的
             返回std:：string（“unlimited”）；
         }
         string unit=“我们”；
         double micro=静态铸造<double>（nmicro）；

         如果（micro>1000000*60*60ll）
            微/=1000000*60*60ll；
            单位=“人力资源”；
         }
         否则，如果（micro>1000000*60）
            微/=1000000*60；
            单位=“闽”；
         }
         否则，如果（micro>1000000）
            微/=1000000；
            单位=“秒”；
         }
         否则，如果（micro>1000）
            微/＝1000；
            单位=“MS”；
         }
         std：：字符串流ss；
         ss<<设定精度（4）；
         ss<<micro<<“”；
         if（宽度\单位>0）
            ss<<std:：left<<setw（宽度_表示_单位）；
         SS <单位；
         返回ss.str（）；
      }；


      std:：cout<<std:：fixed<<setprecision（3）；
      std:：cout<<indent<<std:：left<<std:：setw（11）<“used:”<<std:：right<<std:：setw（18）<<to pretty_net（res.net_limit.used）<<\n“；
      std:：cout<<indent<<std:：left<<std:：setw（11）<“available：”<<std:：right<<std:：setw（18）<<to摷pretty_net（res.net_limit.available）<<\n“；
      std:：cout<<indent<<std:：left<<std:：setw（11）<<limit:“<<std:：right<<std:：setw（18）<<to摷pretty_net（res.net_limit.max）<<\n”；
      std:：cout<<std:：endl；

      std:：cout<“CPU带宽：”<<std:：endl；

      if（res.total_resources.is_object（））
         auto cpu_total=to_asset（res.total_resources.get_object（）[“cpu_weight”]。as_string（））；

         if（res.self_delegated_bandwidth.is_object（））
            asset cpu_own=asset:：from_string（res.self_delegated_bandwidth.get_object（）[“cpu_weight”].as_string（））；
            staked+=cpu拥有；

            auto cpu_others=cpu_total-cpu_own；

            std:：cout<<indent<<staked:“<<std:：setw（20）<<cpu_own
                      <<std:：string（11，“）<<”（从账户委托给自身的总股份）“<<std:：endl
                      <<indent<“委派：”<<std:：setw（17）<<cpu_others
                      <<std:：string（11，“）<<”（total staked delegated to account from others）”<<std:：endl；
         }否则{
            auto cpu_others=cpu_总数；
            std：：cout<<indent<“delegated：”<<std：：setw（17）<<cpu_others
                      <<std:：string（11，“）<<”（total staked delegated to account from others）”<<std:：endl；
         }
      }


      std:：cout<<std:：fixed<<setprecision（3）；
      std:：cout<<indent<<std:：left<<std:：setw（11）<“used:”<<std:：right<<std:：setw（18）<<to prettyou time（res.cpuou limit.used）<<\n“；
      std:：cout<<indent<<std:：left<<std:：setw（11）<“available:”<<std:：right<<std:：setw（18）<<to pretty_time（res.cpu_limit.available）<<\n“；
      std:：cout<<indent<<std:：left<<std:：setw（11）<<limit:“<<std:：right<<std:：setw（18）<<to摷pretty_time（res.cpu_limit.max）<<\n”；
      std:：cout<<std:：endl；

      if（res.rebund_request.is_object（））
         auto obj=res.rebund_request.get_object（）；
         auto-request_time=fc:：time_point_sec:：from_iso_string（obj[“request_time”].as_string（））；
         fc：：时间点退款时间=请求时间+fc：：天（3）；
         auto now=res.head_block_时间；
         asset net=asset:：from_string（obj[“net_amount”].as_string（））；
         asset cpu=asset:：from_string（obj[“cpu_amount”]。as_string（））；
         失稳=net+cpu；

         if（unstaking>资产（0，unstaking.get诳symbol（））
            std:：cout<<std:：fixed<<setprecision（3）；
            std：：cout<“正在取消标记：”<<std：：endl；
            std:：cout<<indent<<std:：left<<std:：setw（25）<<unstake请求时间：“<<std:：right<<std:：setw（20）<<string（请求时间）；
            如果（现在>=退款时间）
               std：：cout<<“（现在可通过‘eosio：：退款’操作进行索赔）\n”；
            }否则{
               std：：cout<“（资金将在“<<to \u pretty \u time（（rebund \u time-now）.count（），0）<”）\n”中提供；
            }
            std：：cout<<indent<<std:：left<<std:：setw（25）<<from net bandwidth:“<<std:：right<<std:：setw（18）<<net<<std:：endl；
            std:：cout<<indent<<std:：left<<std:：setw（25）<“from cpu bandwidth：”<<std:：right<<std:：setw（18）<<cpu<<std:：endl；
            std:：cout<<indent<<std:：left<<std:：setw（25）<<total:“<<std:：right<<std:：setw（18）<<unstaking<<std:：endl；
            std:：cout<<std:：endl；
         }
      }

      if（res.core_liquid_balance.valid（））
         std:：cout<<res.core_liquid_balance->get_symbol（）.name（）<“balances：”<<std:：endl；
         std:：cout<<indent<<std:：left<<std:：setw（11）
                   <“Liquid：”<<Std:：Right<<Std:：Setw（18）<<*Res.Core_Liquid_Balance<<Std:：Endl；
         std:：cout<<indent<<std:：left<<std:：setw（11）
                   <“staked：”<<std:：right<<std:：setw（18）<<staked<<std:：endl；
         std:：cout<<indent<<std:：left<<std:：setw（11）
                   <“Unstaking：”<<std:：right<<std:：setw（18）<<Unstaking<<std:：endl；
         std:：cout<<indent<<std:：left<<std:：setw（11）<<total:“<<std:：right<<std:：setw（18）<（*res.core_liquid_balance+staked+unstaking）<<std:：endl；
         std:：cout<<std:：endl；
      }

      if（res.voter_info.is_object（））
         auto&obj=res.voter_info.get_object（）；
         字符串proxy=obj[“proxy”]。
         if（proxy.empty（））
            auto&prods=obj[“producers”].get_array（）；
            std：：cout<“生产者：”；
            如果（！）prods.empty（））
               对于（int i=0；i<prods.size（）；++i）
                  如果（i%3==0）
                     std:：cout<<std:：endl<<indent；
                  }
                  std:：cout<<std:：setw（16）<<std:：left<<prods[i].as_string（）；
               }
               std:：cout<<std:：endl；
            }否则{
               std：：cout<<indent<<“<not voted>”<<std：：endl；
            }
         }否则{
            std:：cout<“proxy：”<<indent<<proxy<<std:：endl；
         }
      }
      std:：cout<<std:：endl；
   }否则{
      std：：cout<<fc:：json：：to_pretty_string（json）<<std:：endl；
   }
}

cli:：callback_t header_opt_callback=[]（cli:：results_t res）
   vector<string>：：迭代器itr；

   对于（itr=res.begin（）；itr！=res.end（）；itr++）
       头。向后推（*itr）；
   }

   回归真实；
}；

int main（int argc，char**argv）
   setlocale（lc_all，“”）；
   bindtextdomain（locale_域，locale_路径）；
   文本域（区域设置域）；
   context=eosio:：client:：http:：create_http_context（）；
   wallet_url=默认_wallet_url；

   cli:：app app“到eosio客户端的命令行界面”
   app.require_子命令（）；
   app.add_option（“-h，-host”，obsoleted_option_host_port，localized（“运行nodeos的主机”））->group（“hidden”）；
   app.add_option（“-p，-port”，obsoleted_option_host_port，localized（“nodeos运行的端口”））->group（“hidden”）；
   app.add_option（“--wallet host”，obsoleted_option_host_port，localized（“运行keosd的主机”））->group（“hidden”）；
   app.add_option（“--wallet port”，obsoleted_option_host_port，localized（“keosd运行的端口”））->group（“hidden”）；

   app.add_选项（“-u，-url”，url，本地化（“nodeos运行的http/https url”），true）；
   app.add_选项（“--wallet url”，wallet_url，本地化（“keosd运行的http/https url”），真）；

   app.add_选项（“-r，-header”，header_opt_回调，本地化（“通过特定HTTP头；重复此选项以传递多个头”）；
   app.add_flag（“-n，-no verify”，no_verify，localized（“使用https时不验证对等证书”））；
   app.add_flag（“--no auto keosd”，no_auto_keosd，本地化（“如果keosd当前未运行，则不自动启动”）；
   app.set_callback（[&app]确保_keosd_running（&app）；）；

   bool verbose_errors=false；
   app.add_flag（“-v，-verbose”，verbose_errors，localized（“output verbose actions on error”））；
   app.add_标志（“--打印请求”，打印请求，本地化（“打印HTTP请求到stderr”））；
   app.add_标志（“--print response”，print_response，localized（“print http response to stderr”））；

   auto version=app.add_子命令（“version”，本地化（“retrieve version information”），false）；
   version->require_subcommand（）；

   version->add_子命令（“client”，本地化（“retrieve version information of the client”））->set_callback（[]
     std：：cout<<本地化（“构建版本：$ver，”，“（ver”，eosio:：client:：config:：version_str））<<std:：endl；
   （}）；

   //创建子命令
   auto-create=app.add_子命令（“创建”，本地化（“创建各种项目，区块链上下”），false）；
   create->require_subcommand（）；

   bool r1=假；
   字符串键_文件；
   bool print_console=false；
   //创建密钥
   auto create_key=create->add_subcommand（“key”，localized（“创建新的密钥对并打印公钥和私钥”））->set_callback（[&r1，&key_file，&print_console]（）
      如果（key_file.empty（）&&！打印控制台）
         std:：cerr<“错误：使用\”--file\“或传递\”--to console\“”<<std:：endl指示文件；
         返回；
      }

      自动pk=r1？private_key_type:：generate_r1（）：private_key_type:：generate（）；
      auto-privs=字符串（pk）；
      auto pubs=string（pk.get_public_key（））；
      如果（打印控制台）
         std:：cout<<本地化（“private key:$key，”，“（key”，privs））<<std:：endl；
         std：：cout<<本地化（“公钥：$键”，“键”，pubs））<<std：：endl；
      }否则{
         std:：cerr<<localized（“saving keys to$filename”，“（filename”，key_file））<<std:：endl；
         std:：ofstream out（key_file.c_str（））；
         out<<localized（“private key:$key，”，“（key”，privs））<<std:：endl；
         out<<localized（“public key:$key，”，“（key”，pubs））<<std:：endl；
      }
   （}）；
   创建_key->add_flag（“--r1”，r1，“使用r1曲线（iphone）而不是k1曲线（bitcoin）生成密钥”）；
   创建_key->add_选项（“-f，-file”，key_文件，本地化（“要写入私钥/公钥输出的文件名”）。（必须设置，除非传递\“--to console \”“）；
   创建_key->add_flag（“--to console”，打印_console，本地化（“print private/public key to console.”））；

   //创建账户
   auto create account=create_account_子命令（create，true/*simpl*/ );


//转换子命令
auto convert = app.add_subcommand("convert", localized("Pack and unpack transactions"), false); //TODO是否还从这里添加基于ABI的转换操作参数？
   convert->require_subcommand();

//打包事务
   string plain_signed_transaction_json;
   bool pack_action_data_flag = false;
   auto pack_transaction = convert->add_subcommand("pack_transaction", localized("From plain signed json to packed form"));
   pack_transaction->add_option("transaction", plain_signed_transaction_json, localized("The plain signed json (string)"))->required();
   pack_transaction->add_flag("--pack-action-data", pack_action_data_flag, localized("Pack all action data within transaction, needs interaction with nodeos"));
   pack_transaction->set_callback([&] {
      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string( plain_signed_transaction_json );
      } EOS_RETHROW_EXCEPTIONS( transaction_type_exception, "Fail to parse plain transaction JSON '${data}'", ("data", plain_signed_transaction_json))
      if( pack_action_data_flag ) {
         signed_transaction trx;
         abi_serializer::from_variant( trx_var, trx, abi_serializer_resolver, abi_serializer_max_time );
         std::cout << fc::json::to_pretty_string( packed_transaction( trx, packed_transaction::none )) << std::endl;
      } else {
         try {
            signed_transaction trx = trx_var.as<signed_transaction>();
            std::cout << fc::json::to_pretty_string( fc::variant( packed_transaction( trx, packed_transaction::none ))) << std::endl;
         } EOS_RETHROW_EXCEPTIONS( transaction_type_exception, "Fail to convert transaction, --pack-action-data likely needed" )
      }
   });

//解包事务
   string packed_transaction_json;
   bool unpack_action_data_flag = false;
   auto unpack_transaction = convert->add_subcommand("unpack_transaction", localized("From packed to plain signed json form"));
   unpack_transaction->add_option("transaction", packed_transaction_json, localized("The packed transaction json (string containing packed_trx and optionally compression fields)"))->required();
   unpack_transaction->add_flag("--unpack-action-data", unpack_action_data_flag, localized("Unpack all action data within transaction, needs interaction with nodeos"));
   unpack_transaction->set_callback([&] {
      fc::variant packed_trx_var;
      packed_transaction packed_trx;
      try {
         packed_trx_var = json_from_file_or_string( packed_transaction_json );
         fc::from_variant<packed_transaction>( packed_trx_var, packed_trx );
      } EOS_RETHROW_EXCEPTIONS( transaction_type_exception, "Fail to parse packed transaction JSON '${data}'", ("data", packed_transaction_json))
      signed_transaction strx = packed_trx.get_signed_transaction();
      fc::variant trx_var;
      if( unpack_action_data_flag ) {
         abi_serializer::to_variant( strx, trx_var, abi_serializer_resolver, abi_serializer_max_time );
      } else {
         trx_var = strx;
      }
      std::cout << fc::json::to_pretty_string( trx_var ) << std::endl;
   });

//打包操作数据
   string unpacked_action_data_account_string;
   string unpacked_action_data_name_string;
   string unpacked_action_data_string;
   auto pack_action_data = convert->add_subcommand("pack_action_data", localized("From json action data to packed form"));
   pack_action_data->add_option("account", unpacked_action_data_account_string, localized("The name of the account that hosts the contract"))->required();
   pack_action_data->add_option("name", unpacked_action_data_name_string, localized("The name of the function that's called by this action"))->required();
   pack_action_data->add_option("unpacked_action_data", unpacked_action_data_string, localized("The action data expressed as json"))->required();
   pack_action_data->set_callback([&] {
      fc::variant unpacked_action_data_json;
      try {
         unpacked_action_data_json = json_from_file_or_string(unpacked_action_data_string);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse unpacked action data JSON")
      bytes packed_action_data_string = variant_to_bin(unpacked_action_data_account_string, unpacked_action_data_name_string, unpacked_action_data_json);
      std::cout << fc::to_hex(packed_action_data_string.data(), packed_action_data_string.size()) << std::endl;
   });

//解压缩操作数据
   string packed_action_data_account_string;
   string packed_action_data_name_string;
   string packed_action_data_string;
   auto unpack_action_data = convert->add_subcommand("unpack_action_data", localized("From packed to json action data form"));
   unpack_action_data->add_option("account", packed_action_data_account_string, localized("The name of the account that hosts the contract"))->required();
   unpack_action_data->add_option("name", packed_action_data_name_string, localized("The name of the function that's called by this action"))->required();
   unpack_action_data->add_option("packed_action_data", packed_action_data_string, localized("The action data expressed as packed hex string"))->required();
   unpack_action_data->set_callback([&] {
      EOS_ASSERT( packed_action_data_string.size() >= 2, transaction_type_exception, "No packed_action_data found" );
      vector<char> packed_action_data_blob(packed_action_data_string.size()/2);
      fc::from_hex(packed_action_data_string, packed_action_data_blob.data(), packed_action_data_blob.size());
      fc::variant unpacked_action_data_json = bin_to_variant(packed_action_data_account_string, packed_action_data_name_string, packed_action_data_blob);
      std::cout << fc::json::to_pretty_string(unpacked_action_data_json) << std::endl;
   });

//获取子命令
   auto get = app.add_subcommand("get", localized("Retrieve various items and information from the blockchain"), false);
   get->require_subcommand();

//获取信息
   get->add_subcommand("info", localized("Get current blockchain information"))->set_callback([] {
      std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
   });

//获取块
   string blockArg;
   bool get_bhs = false;
   auto getBlock = get->add_subcommand("block", localized("Retrieve a full block from the blockchain"), false);
   getBlock->add_option("block", blockArg, localized("The number or ID of the block to retrieve"))->required();
   getBlock->add_flag("--header-state", get_bhs, localized("Get block header state from fork database instead") );
   getBlock->set_callback([&blockArg,&get_bhs] {
      auto arg = fc::mutable_variant_object("block_num_or_id", blockArg);
      if( get_bhs ) {
         std::cout << fc::json::to_pretty_string(call(get_block_header_state_func, arg)) << std::endl;
      } else {
         std::cout << fc::json::to_pretty_string(call(get_block_func, arg)) << std::endl;
      }
   });

//获得帐户
   string accountName;
   string coresym;
   bool print_json;
   auto getAccount = get->add_subcommand("account", localized("Retrieve an account from the blockchain"), false);
   getAccount->add_option("name", accountName, localized("The name of the account to retrieve"))->required();
   getAccount->add_option("core-symbol", coresym, localized("The expected core symbol of the chain you are querying"));
   getAccount->add_flag("--json,-j", print_json, localized("Output in JSON format") );
   getAccount->set_callback([&]() { get_account(accountName, coresym, print_json); });

//获取代码
   string codeFilename;
   string abiFilename;
   bool code_as_wasm = false;
   auto getCode = get->add_subcommand("code", localized("Retrieve the code and ABI for an account"), false);
   getCode->add_option("name", accountName, localized("The name of the account whose code should be retrieved"))->required();
   getCode->add_option("-c,--code",codeFilename, localized("The name of the file to save the contract .wast/wasm to") );
   getCode->add_option("-a,--abi",abiFilename, localized("The name of the file to save the contract .abi to") );
   getCode->add_flag("--wasm", code_as_wasm, localized("Save contract as wasm"));
   getCode->set_callback([&] {
      string code_hash, wasm, wast, abi;
      try {
         const auto result = call(get_raw_code_and_abi_func, fc::mutable_variant_object("account_name", accountName));
         const std::vector<char> wasm_v = result["wasm"].as_blob().data;
         const std::vector<char> abi_v = result["abi"].as_blob().data;

         fc::sha256 hash;
         if(wasm_v.size())
            hash = fc::sha256::hash(wasm_v.data(), wasm_v.size());
         code_hash = (string)hash;

         wasm = string(wasm_v.begin(), wasm_v.end());
         if(!code_as_wasm && wasm_v.size())
            wast = wasm_to_wast((const uint8_t*)wasm_v.data(), wasm_v.size(), false);

         abi_def abi_d;
         if(abi_serializer::to_abi(abi_v, abi_d))
            abi = fc::json::to_pretty_string(abi_d);
      }
      catch(chain::missing_chain_api_plugin_exception&) {
//看看这是不是一个不支持获取原始代码的老节点
         const auto old_result = call(get_code_func, fc::mutable_variant_object("account_name", accountName)("code_as_wasm",code_as_wasm));
         code_hash = old_result["code_hash"].as_string();
         if(code_as_wasm) {
            wasm = old_result["wasm"].as_string();
            std::cout << localized("Warning: communicating to older nodeos which returns malformed binary wasm") << std::endl;
         }
         else
            wast = old_result["wast"].as_string();
         abi = fc::json::to_pretty_string(old_result["abi"]);
      }

      std::cout << localized("code hash: ${code_hash}", ("code_hash", code_hash)) << std::endl;

      if( codeFilename.size() ){
         std::cout << localized("saving ${type} to ${codeFilename}", ("type", (code_as_wasm ? "wasm" : "wast"))("codeFilename", codeFilename)) << std::endl;

         std::ofstream out( codeFilename.c_str() );
         if(code_as_wasm)
            out << wasm;
         else
            out << wast;
      }
      if( abiFilename.size() ) {
         std::cout << localized("saving abi to ${abiFilename}", ("abiFilename", abiFilename)) << std::endl;
         std::ofstream abiout( abiFilename.c_str() );
         abiout << abi;
      }
   });

//获得ABI
   string filename;
   auto getAbi = get->add_subcommand("abi", localized("Retrieve the ABI for an account"), false);
   getAbi->add_option("name", accountName, localized("The name of the account whose abi should be retrieved"))->required();
   getAbi->add_option("-f,--file",filename, localized("The name of the file to save the contract .abi to instead of writing to console") );
   getAbi->set_callback([&] {
      auto result = call(get_abi_func, fc::mutable_variant_object("account_name", accountName));

      auto abi  = fc::json::to_pretty_string( result["abi"] );
      if( filename.size() ) {
         std::cerr << localized("saving abi to ${filename}", ("filename", filename)) << std::endl;
         std::ofstream abiout( filename.c_str() );
         abiout << abi;
      } else {
         std::cout << abi << "\n";
      }
   });

//获得表
   string scope;
   string code;
   string table;
   string lower;
   string upper;
   string table_key;
   string key_type;
   string encode_type{"dec"};
   bool binary = false;
   uint32_t limit = 10;
   string index_position;
   bool reverse = false;
   bool show_payer = false;
   auto getTable = get->add_subcommand( "table", localized("Retrieve the contents of a database table"), false);
   getTable->add_option( "account", code, localized("The account who owns the table") )->required();
   getTable->add_option( "scope", scope, localized("The scope within the contract in which the table is found") )->required();
   getTable->add_option( "table", table, localized("The name of the table as specified by the contract abi") )->required();
   getTable->add_option( "-b,--binary", binary, localized("Return the value as BINARY rather than using abi to interpret as JSON") );
   getTable->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") );
   getTable->add_option( "-k,--key", table_key, localized("Deprecated") );
   getTable->add_option( "-L,--lower", lower, localized("JSON representation of lower bound value of key, defaults to first") );
   getTable->add_option( "-U,--upper", upper, localized("JSON representation of upper bound value of key, defaults to last") );
   getTable->add_option( "--index", index_position,
                         localized("Index number, 1 - primary (first), 2 - secondary index (in order defined by multi_index), 3 - third index, etc.\n"
                                   "\t\t\t\tNumber or name of index can be specified, e.g. 'secondary' or '2'."));
   getTable->add_option( "--key-type", key_type,
                         localized("The key type of --index, primary only supports (i64), all others support (i64, i128, i256, float64, float128, ripemd160, sha256).\n"
                                   "\t\t\t\tSpecial type 'name' indicates an account name."));
   getTable->add_option( "--encode-type", encode_type,
                         localized("The encoding type of key_type (i64 , i128 , float64, float128) only support decimal encoding e.g. 'dec'"
                                    "i256 - supports both 'dec' and 'hex', ripemd160 and sha256 is 'hex' only"));
   getTable->add_flag("-r,--reverse", reverse, localized("Iterate in reverse order"));
   getTable->add_flag("--show-payer", show_payer, localized("show RAM payer"));


   getTable->set_callback([&] {
      auto result = call(get_table_func, fc::mutable_variant_object("json", !binary)
                         ("code",code)
                         ("scope",scope)
                         ("table",table)
("table_key",table_key) //不使用
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
                         ("key_type",key_type)
                         ("index_position", index_position)
                         ("encode_type", encode_type)
                         ("reverse", reverse)
                         ("show_payer", show_payer)
                         );

      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
   });

   auto getScope = get->add_subcommand( "scope", localized("Retrieve a list of scopes and tables owned by a contract"), false);
   getScope->add_option( "contract", code, localized("The contract who owns the table") )->required();
   getScope->add_option( "-t,--table", table, localized("The name of the table as filter") );
   getScope->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") );
   getScope->add_option( "-L,--lower", lower, localized("lower bound of scope") );
   getScope->add_option( "-U,--upper", upper, localized("upper bound of scope") );
   getScope->add_flag("-r,--reverse", reverse, localized("Iterate in reverse order"));
   getScope->set_callback([&] {
      auto result = call(get_table_by_scope_func, fc::mutable_variant_object("code",code)
                         ("table",table)
                         ("lower_bound",lower)
                         ("upper_bound",upper)
                         ("limit",limit)
                         ("reverse", reverse)
                         );
      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
   });

//货币存取器
//获取货币余额
   string symbol;
   auto get_currency = get->add_subcommand( "currency", localized("Retrieve information related to standard currencies"), true);
   get_currency->require_subcommand();
   auto get_balance = get_currency->add_subcommand( "balance", localized("Retrieve the balance of an account for a given currency"), false);
   get_balance->add_option( "contract", code, localized("The contract that operates the currency") )->required();
   get_balance->add_option( "account", accountName, localized("The account to query balances for") )->required();
   get_balance->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") );
   get_balance->set_callback([&] {
      auto result = call(get_currency_balance_func, fc::mutable_variant_object
         ("account", accountName)
         ("code", code)
         ("symbol", symbol.empty() ? fc::variant() : symbol)
      );

      const auto& rows = result.get_array();
      for( const auto& r : rows ) {
         std::cout << r.as_string()
                   << std::endl;
      }
   });

   auto get_currency_stats = get_currency->add_subcommand( "stats", localized("Retrieve the stats of for a given currency"), false);
   get_currency_stats->add_option( "contract", code, localized("The contract that operates the currency") )->required();
   get_currency_stats->add_option( "symbol", symbol, localized("The symbol for the currency if the contract operates multiple currencies") )->required();
   get_currency_stats->set_callback([&] {
      auto result = call(get_currency_stats_func, fc::mutable_variant_object("json", false)
         ("code", code)
         ("symbol", symbol)
      );

      std::cout << fc::json::to_pretty_string(result)
                << std::endl;
   });

//结帐
   string public_key_str;
   auto getAccounts = get->add_subcommand("accounts", localized("Retrieve accounts associated with a public key"), false);
   getAccounts->add_option("public_key", public_key_str, localized("The public key to retrieve accounts for"))->required();
   getAccounts->set_callback([&] {
      public_key_type public_key;
      try {
         public_key = public_key_type(public_key_str);
      } EOS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", public_key_str))
      auto arg = fc::mutable_variant_object( "public_key", public_key);
      std::cout << fc::json::to_pretty_string(call(get_key_accounts_func, arg)) << std::endl;
   });


//得到仆人
   string controllingAccount;
   auto getServants = get->add_subcommand("servants", localized("Retrieve accounts which are servants of a given account "), false);
   getServants->add_option("account", controllingAccount, localized("The name of the controlling account"))->required();
   getServants->set_callback([&] {
      auto arg = fc::mutable_variant_object( "controlling_account", controllingAccount);
      std::cout << fc::json::to_pretty_string(call(get_controlled_accounts_func, arg)) << std::endl;
   });

//获取事务
   string transaction_id_str;
   uint32_t block_num_hint = 0;
   auto getTransaction = get->add_subcommand("transaction", localized("Retrieve a transaction from the blockchain"), false);
   getTransaction->add_option("id", transaction_id_str, localized("ID of the transaction to retrieve"))->required();
   getTransaction->add_option( "-b,--block-hint", block_num_hint, localized("the block number this transaction may be in") );
   getTransaction->set_callback([&] {
      auto arg= fc::mutable_variant_object( "id", transaction_id_str);
      if ( block_num_hint > 0 ) {
         arg = arg("block_num_hint", block_num_hint);
      }
      std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl;
   });

//获得行动
   string account_name;
   string skip_seq_str;
   string num_seq_str;
   bool printjson = false;
   bool fullact = false;
   bool prettyact = false;
   bool printconsole = false;

   int32_t pos_seq = -1;
   int32_t offset = -20;
   auto getActions = get->add_subcommand("actions", localized("Retrieve all actions with specific account name referenced in authorization or receiver"), false);
   getActions->add_option("account_name", account_name, localized("name of account to query on"))->required();
   getActions->add_option("pos", pos_seq, localized("sequence number of action for this account, -1 for last"));
   getActions->add_option("offset", offset, localized("get actions [pos,pos+offset] for positive offset or [pos-offset,pos) for negative offset"));
   getActions->add_flag("--json,-j", printjson, localized("print full json"));
   getActions->add_flag("--full", fullact, localized("don't truncate action json"));
   getActions->add_flag("--pretty", prettyact, localized("pretty print full action json "));
   getActions->add_flag("--console", printconsole, localized("print console output generated by action "));
   getActions->set_callback([&] {
      fc::mutable_variant_object arg;
      arg( "account_name", account_name );
      arg( "pos", pos_seq );
      arg( "offset", offset);

      auto result = call(get_actions_func, arg);


      if( printjson ) {
         std::cout << fc::json::to_pretty_string(result) << std::endl;
      } else {
          auto& traces = result["actions"].get_array();
          uint32_t lib = result["last_irreversible_block"].as_uint64();


          cout  << "#" << setw(5) << "seq" << "  " << setw( 24 ) << left << "when"<< "  " << setw(24) << right << "contract::action" << " => " << setw(13) << left << "receiver" << " " << setw(11) << left << "trx id..." << " args\n";
          cout  << "================================================================================================================\n";
          for( const auto& trace: traces ) {
              std::stringstream out;
              if( trace["block_num"].as_uint64() <= lib )
                 out << "#";
              else
                 out << "?";

              out << setw(5) << trace["account_action_seq"].as_uint64() <<"  ";
              out << setw(24) << trace["block_time"].as_string() <<"  ";

              const auto& at = trace["action_trace"].get_object();

              auto id = at["trx_id"].as_string();
              const auto& receipt = at["receipt"];
              auto receiver = receipt["receiver"].as_string();
              const auto& act = at["act"].get_object();
              auto code = act["account"].as_string();
              auto func = act["name"].as_string();
              string args;
              if( prettyact ) {
                  args = fc::json::to_pretty_string( act["data"] );
              }
              else {
                 args = fc::json::to_string( act["data"] );
                 if( !fullact ) {
                    args = args.substr(0,60) + "...";
                 }
              }
              out << std::setw(24) << std::right<< (code +"::" + func) << " => " << left << std::setw(13) << receiver;

              out << " " << setw(11) << (id.substr(0,8) + "...");

              if( fullact || prettyact ) out << "\n";
              else out << " ";

out << args ;//< \n；

              if( trace["block_num"].as_uint64() <= lib ) {
                 dlog( "\r${m}", ("m",out.str()) );
              } else {
                 wlog( "\r${m}", ("m",out.str()) );
              }
              if( printconsole ) {
                 auto console = at["console"].as_string();
                 if( console.size() ) {
                    stringstream out;
                    std::stringstream ss(console);
                    string line;
                    std::getline( ss, line );
                    out << ">> " << line << "\n";
cerr << out.str(); //ilog（“\r$m”，“（m”，out.str（））；
                 }
              }
          }
      }
   });

   auto getSchedule = get_schedule_subcommand{get};
   auto getTransactionId = get_transaction_id_subcommand{get};

   /*
   auto getTransactions=get->add_子命令（“transactions”，本地化（“retrieve all transactions with specific account name referenced in their scope”），false）；
   getTransactions->add_option（“account_name”，account_name，localized（“要查询的帐户名”））->required（）；
   getTransactions->add_option（“skip_seq”，skip_seq_str，localized（“要跳过的最新事务数（0将从最新事务开始）”））；
   getTransactions->add_option（“num_seq”，num_seq_str，本地化（“要返回的事务数”））；
   getTransactions->add_flag（“--json，-j”，print json，本地化（“print full json”））；
   getTransactions->set_回调（[&]
      fc：：可变变量_对象arg；
      if（skip_seq_str.empty（））
         arg=fc：：可变的_variant_对象（“account_name”，account_name）；
      }否则{
         uint64_t跳过序列；
         尝试{
            skip_seq=boost:：lexical_cast<uint64_t>（skip_seq_str）；
         eos_rethrow_exceptions（chain_type_exception，“invalid skip seq:$skip_seq”，（“skip_seq”，skip_seq_str））。
         if（num_seq_str.empty（））
            arg=fc：：可变的_variant_对象（“account_name”，account_name）（“skip_seq”，skip_seq）；
         }否则{
            uint64编号：
            尝试{
               num_seq=boost:：lexical_cast<uint64_t>（num_seq_str）；
            eos_rethrow_exceptions（chain_type_exception，“invalid num seq:$num_seq”，（“num_seq”，num_seq_str））。
            arg=fc：：可变的_variant_object（“account_name”，account_name）（“skip_seq”，“skip_seq_str”）（num_seq”，num_seq）；
         }
      }
      自动结果=调用（get_transactions_func，arg）；
      如果（printjson）
         std:：cout<<fc:：json:：to_pretty_string（result）<<std:：endl；
      }
      否则{
         const auto&trxs=result.get_object（）[“transactions”].get_array（）；
         用于（const auto&t:trxs）

            const auto&tobj=t.get_object（）；
            const auto&trx=tobj[“transaction”]。
            const auto&data=trx[“transaction”].get_object（）；
            const auto&msgs=data[“actions”]。

            用于（const auto&msg:msgs）
               int64_t seq_num=tobj[“seq_num”]。
               string id=tobj[“transaction_id”].作为_string（）；
               const auto&exp=data[“expiration”]。
               std：：cout<<tobj[“seq_num”]。as_string（）<<“]”<<id.substr（0,8）<<“…”<<data[“expiration”]。as_string（）<“'；
               auto code=msg[“account”]。
               auto func=msg[“name”]。
               auto args=fc:：json:：to_string（msg[“data”]）；
               std：：cout<<setw（26）<<left<（code+“”：“+func）<<”“<<args；
               std:：cout<<std:：endl；
            }
         }
      }

   （}）；
   **/


//设置子命令
   auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state"));
   setSubcommand->require_subcommand();

//设置合同子命令
   string account;
   string contractPath;
   string wasmPath;
   string abiPath;
   bool shouldSend = true;
   bool contract_clear = false;
   bool suppress_duplicate_check = false;
   auto codeSubcommand = setSubcommand->add_subcommand("code", localized("Create or update the code on an account"));
   codeSubcommand->add_option("account", account, localized("The account to set code for"))->required();
codeSubcommand->add_option("code-file", wasmPath, localized("The fullpath containing the contract WASM"));//->要求（）；
   codeSubcommand->add_flag( "-c,--clear", contract_clear, localized("Remove code on an account"));
   codeSubcommand->add_flag( "--suppress-duplicate-check", suppress_duplicate_check, localized("Don't check for duplicate"));

   auto abiSubcommand = setSubcommand->add_subcommand("abi", localized("Create or update the abi on an account"));
   abiSubcommand->add_option("account", account, localized("The account to set the ABI for"))->required();
abiSubcommand->add_option("abi-file", abiPath, localized("The fullpath containing the contract ABI"));//->要求（）；
   abiSubcommand->add_flag( "-c,--clear", contract_clear, localized("Remove abi on an account"));
   abiSubcommand->add_flag( "--suppress-duplicate-check", suppress_duplicate_check, localized("Don't check for duplicate"));

   auto contractSubcommand = setSubcommand->add_subcommand("contract", localized("Create or update the contract on an account"));
   contractSubcommand->add_option("account", account, localized("The account to publish a contract for"))
                     ->required();
   contractSubcommand->add_option("contract-dir", contractPath, localized("The path containing the .wasm and .abi"));
//->要求（）；
   contractSubcommand->add_option("wasm-file", wasmPath, localized("The file containing the contract WASM relative to contract-dir"));
//->检查（cli:：existingfile）；
   auto abi = contractSubcommand->add_option("abi-file,-a,--abi", abiPath, localized("The ABI for the contract relative to contract-dir"));
//->检查（cli:：existingfile）；
   contractSubcommand->add_flag( "-c,--clear", contract_clear, localized("Rmove contract on an account"));
   contractSubcommand->add_flag( "--suppress-duplicate-check", suppress_duplicate_check, localized("Don't check for duplicate"));

   std::vector<chain::action> actions;
   auto set_code_callback = [&]() {

      std::vector<char> old_wasm;
      bool duplicate = false;
      fc::sha256 old_hash, new_hash;
      if (!suppress_duplicate_check) {
         try {
            const auto result = call(get_code_hash_func, fc::mutable_variant_object("account_name", account));
            old_hash = fc::sha256(result["code_hash"].as_string());
         } catch (...) {
            std::cerr << "Failed to get existing code hash, continue without duplicate check..." << std::endl;
            suppress_duplicate_check = true;
         }
      }

      bytes code_bytes;
      if(!contract_clear){
        std::string wasm;
        fc::path cpath = fc::canonical(fc::path(contractPath));

        if( wasmPath.empty() )
           wasmPath = (cpath / (cpath.filename().generic_string()+".wasm")).generic_string();
        else
           wasmPath = (cpath / wasmPath).generic_string();

        std::cerr << localized(("Reading WASM from " + wasmPath + "...").c_str()) << std::endl;
        fc::read_file_contents(wasmPath, wasm);
        EOS_ASSERT( !wasm.empty(), wasm_file_not_found, "no wasm file found ${f}", ("f", wasmPath) );

        const string binary_wasm_header("\x00\x61\x73\x6d\x01\x00\x00\x00", 8);
        if(wasm.compare(0, 8, binary_wasm_header))
           std::cerr << localized("WARNING: ") << wasmPath << localized(" doesn't look like a binary WASM file. Is it something else, like WAST? Trying anyways...") << std::endl;
        code_bytes = bytes(wasm.begin(), wasm.end());
      } else {
        code_bytes = bytes();
      }

      if (!suppress_duplicate_check) {
         if (code_bytes.size()) {
            new_hash = fc::sha256::hash(&(code_bytes[0]), code_bytes.size());
         }
         duplicate = (old_hash == new_hash);
      }

      if (!duplicate) {
         actions.emplace_back( create_setcode(account, code_bytes ) );
         if ( shouldSend ) {
            std::cerr << localized("Setting Code...") << std::endl;
            send_actions(std::move(actions), 10000, packed_transaction::zlib);
         }
      } else {
         std::cerr << localized("Skipping set code because the new code is the same as the existing code") << std::endl;
      }
   };

   auto set_abi_callback = [&]() {

      bytes old_abi;
      bool duplicate = false;
      if (!suppress_duplicate_check) {
         try {
            const auto result = call(get_raw_abi_func, fc::mutable_variant_object("account_name", account));
            old_abi = result["abi"].as_blob().data;
         } catch (...) {
            std::cerr << "Failed to get existing raw abi, continue without duplicate check..." << std::endl;
            suppress_duplicate_check = true;
         }
      }

      bytes abi_bytes;
      if(!contract_clear){
        fc::path cpath = fc::canonical(fc::path(contractPath));

        if( abiPath.empty() ) {
           abiPath = (cpath / (cpath.filename().generic_string()+".abi")).generic_string();
        } else {
           abiPath = (cpath / abiPath).generic_string();
        }

        EOS_ASSERT( fc::exists( abiPath ), abi_file_not_found, "no abi file found ${f}", ("f", abiPath)  );

        abi_bytes = fc::raw::pack(fc::json::from_file(abiPath).as<abi_def>());
      } else {
        abi_bytes = bytes();
      }

      if (!suppress_duplicate_check) {
         duplicate = (old_abi.size() == abi_bytes.size() && std::equal(old_abi.begin(), old_abi.end(), abi_bytes.begin()));
      }

      if (!duplicate) {
         try {
            actions.emplace_back( create_setabi(account, abi_bytes) );
         } EOS_RETHROW_EXCEPTIONS(abi_type_exception,  "Fail to parse ABI JSON")
         if ( shouldSend ) {
            std::cerr << localized("Setting ABI...") << std::endl;
            send_actions(std::move(actions), 10000, packed_transaction::zlib);
         }
      } else {
         std::cerr << localized("Skipping set abi because the new abi is the same as the existing abi") << std::endl;
      }
   };

   add_standard_transaction_options(contractSubcommand, "account@active");
   add_standard_transaction_options(codeSubcommand, "account@active");
   add_standard_transaction_options(abiSubcommand, "account@active");
   contractSubcommand->set_callback([&] {
      if(!contract_clear) EOS_ASSERT( !contractPath.empty(), contract_exception, " contract-dir is null ", ("f", contractPath) );
      shouldSend = false;
      set_code_callback();
      set_abi_callback();
      if (actions.size()) {
         std::cerr << localized("Publishing contract...") << std::endl;
         send_actions(std::move(actions), 10000, packed_transaction::zlib);
      } else {
         std::cout << "no transaction is sent" << std::endl;
      }
   });
   codeSubcommand->set_callback(set_code_callback);
   abiSubcommand->set_callback(set_abi_callback);

//设定帐户
   auto setAccount = setSubcommand->add_subcommand("account", localized("set or update blockchain account state"))->require_subcommand();

//设置帐户权限
   auto setAccountPermission = set_account_permission_subcommand(setAccount);

//设定动作
   auto setAction = setSubcommand->add_subcommand("action", localized("set or update blockchain action state"))->require_subcommand();

//设置操作权限
   auto setActionPermission = set_action_permission_subcommand(setAction);

//传输子命令
   string con = "eosio.token";
   string sender;
   string recipient;
   string amount;
   string memo;
   bool pay_ram = false;
   auto transfer = app.add_subcommand("transfer", localized("Transfer tokens from account to account"), false);
   transfer->add_option("sender", sender, localized("The account sending tokens"))->required();
   transfer->add_option("recipient", recipient, localized("The account receiving tokens"))->required();
   transfer->add_option("amount", amount, localized("The amount of tokens to send"))->required();
   transfer->add_option("memo", memo, localized("The memo for the transfer"));
   transfer->add_option("--contract,-c", con, localized("The contract which controls the token"));
   transfer->add_flag("--pay-ram-to-open", pay_ram, localized("Pay ram to open recipient's token balance row"));

   add_standard_transaction_options(transfer, "sender@active");
   transfer->set_callback([&] {
      if (tx_force_unique && memo.size() == 0) {
//使用备忘录添加一个nonce
         memo = generate_nonce_string();
         tx_force_unique = false;
      }

      auto transfer_amount = to_asset(con, amount);
      auto transfer = create_transfer(con, sender, recipient, transfer_amount, memo);
      if (!pay_ram) {
         send_actions( { transfer });
      } else {
         auto open_ = create_open(con, recipient, transfer_amount.get_symbol(), sender);
         send_actions( { open_, transfer } );
      }
   });

//网络子命令
   string new_host;
   auto net = app.add_subcommand( "net", localized("Interact with local p2p network connections"), false );
   net->require_subcommand();
   auto connect = net->add_subcommand("connect", localized("start a new connection to a peer"), false);
   connect->add_option("host", new_host, localized("The hostname:port to connect to."))->required();
   connect->set_callback([&] {
      const auto& v = call(url, net_connect, new_host);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto disconnect = net->add_subcommand("disconnect", localized("close an existing connection"), false);
   disconnect->add_option("host", new_host, localized("The hostname:port to disconnect from."))->required();
   disconnect->set_callback([&] {
      const auto& v = call(url, net_disconnect, new_host);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto status = net->add_subcommand("status", localized("status of existing connection"), false);
   status->add_option("host", new_host, localized("The hostname:port to query status of connection"))->required();
   status->set_callback([&] {
      const auto& v = call(url, net_status, new_host);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto connections = net->add_subcommand("peers", localized("status of all existing peers"), false);
   connections->set_callback([&] {
      const auto& v = call(url, net_connections, new_host);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });



//电子钱包子命令
   auto wallet = app.add_subcommand( "wallet", localized("Interact with local wallet"), false );
   wallet->require_subcommand();
//创建钱包
   string wallet_name = "default";
   string password_file;
   auto createWallet = wallet->add_subcommand("create", localized("Create a new wallet locally"), false);
   createWallet->add_option("-n,--name", wallet_name, localized("The name of the new wallet"), true);
   createWallet->add_option("-f,--file", password_file, localized("Name of file to write wallet password output to. (Must be set, unless \"--to-console\" is passed"));
   createWallet->add_flag( "--to-console", print_console, localized("Print password to console."));
   createWallet->set_callback([&wallet_name, &password_file, &print_console] {
      EOSC_ASSERT( !password_file.empty() ^ print_console, "ERROR: Either indicate a file using \"--file\" or pass \"--to-console\"" );
      EOSC_ASSERT( password_file.empty() || !std::ofstream(password_file.c_str()).fail(), "ERROR: Failed to create file in specified path" );

      const auto& v = call(wallet_url, wallet_create, wallet_name);
      std::cout << localized("Creating wallet: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
      std::cout << localized("Save password to use in the future to unlock this wallet.") << std::endl;
      std::cout << localized("Without password imported keys will not be retrievable.") << std::endl;
      if (print_console) {
         std::cout << fc::json::to_pretty_string(v) << std::endl;
      } else {
         std::cerr << localized("saving password to ${filename}", ("filename", password_file)) << std::endl;
         auto password_str = fc::json::to_pretty_string(v);
         boost::replace_all(password_str, "\"", "");
         std::ofstream out( password_file.c_str() );
         out << password_str;
      }
   });

//打开钱包
   auto openWallet = wallet->add_subcommand("open", localized("Open an existing wallet"), false);
   openWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to open"));
   openWallet->set_callback([&wallet_name] {
      call(wallet_url, wallet_open, wallet_name);
      std::cout << localized("Opened: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
   });

//锁钱包
   auto lockWallet = wallet->add_subcommand("lock", localized("Lock wallet"), false);
   lockWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to lock"));
   lockWallet->set_callback([&wallet_name] {
      call(wallet_url, wallet_lock, wallet_name);
      std::cout << localized("Locked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
   });

//锁定所有钱包
   auto locakAllWallets = wallet->add_subcommand("lock_all", localized("Lock all unlocked wallets"), false);
   locakAllWallets->set_callback([] {
      call(wallet_url, wallet_lock_all);
      std::cout << localized("Locked All Wallets") << std::endl;
   });

//解锁钱包
   string wallet_pw;
   auto unlockWallet = wallet->add_subcommand("unlock", localized("Unlock wallet"), false);
   unlockWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to unlock"));
   unlockWallet->add_option("--password", wallet_pw, localized("The password returned by wallet create"));
   unlockWallet->set_callback([&wallet_name, &wallet_pw] {
      prompt_for_wallet_password(wallet_pw, wallet_name);

      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
      call(wallet_url, wallet_unlock, vs);
      std::cout << localized("Unlocked: ${wallet_name}", ("wallet_name", wallet_name)) << std::endl;
   });

//将钥匙导入钱包
   string wallet_key_str;
   auto importWallet = wallet->add_subcommand("import", localized("Import private key into wallet"), false);
   importWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to import key into"));
   importWallet->add_option("--private-key", wallet_key_str, localized("Private key in WIF format to import"));
   importWallet->set_callback([&wallet_name, &wallet_key_str] {
      if( wallet_key_str.size() == 0 ) {
         std::cout << localized("private key: ");
         fc::set_console_echo(false);
         std::getline( std::cin, wallet_key_str, '\n' );
         fc::set_console_echo(true);
      }

      private_key_type wallet_key;
      try {
         wallet_key = private_key_type( wallet_key_str );
      } catch (...) {
         EOS_THROW(private_key_type_exception, "Invalid private key: ${private_key}", ("private_key", wallet_key_str))
      }
      public_key_type pubkey = wallet_key.get_public_key();

      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_key)};
      call(wallet_url, wallet_import_key, vs);
      std::cout << localized("imported private key for: ${pubkey}", ("pubkey", std::string(pubkey))) << std::endl;
   });

//从钱包中取出钥匙
   string wallet_rm_key_str;
   auto removeKeyWallet = wallet->add_subcommand("remove_key", localized("Remove key from wallet"), false);
   removeKeyWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to remove key from"));
   removeKeyWallet->add_option("key", wallet_rm_key_str, localized("Public key in WIF format to remove"))->required();
   removeKeyWallet->add_option("--password", wallet_pw, localized("The password returned by wallet create"));
   removeKeyWallet->set_callback([&wallet_name, &wallet_pw, &wallet_rm_key_str] {
      prompt_for_wallet_password(wallet_pw, wallet_name);
      public_key_type pubkey;
      try {
         pubkey = public_key_type( wallet_rm_key_str );
      } catch (...) {
         EOS_THROW(public_key_type_exception, "Invalid public key: ${public_key}", ("public_key", wallet_rm_key_str))
      }
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw), fc::variant(wallet_rm_key_str)};
      call(wallet_url, wallet_remove_key, vs);
      std::cout << localized("removed private key for: ${pubkey}", ("pubkey", wallet_rm_key_str)) << std::endl;
   });

//在钱包中创建密钥
   string wallet_create_key_type;
   auto createKeyInWallet = wallet->add_subcommand("create_key", localized("Create private key within wallet"), false);
   createKeyInWallet->add_option("-n,--name", wallet_name, localized("The name of the wallet to create key into"), true);
   createKeyInWallet->add_option("key_type", wallet_create_key_type, localized("Key type to create (K1/R1)"), true)->set_type_name("K1/R1");
   createKeyInWallet->set_callback([&wallet_name, &wallet_create_key_type] {
//允许使用空的密钥类型--它将允许基础钱包选择它喜欢的类型
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_create_key_type)};
      const auto& v = call(wallet_url, wallet_create_key, vs);
      std::cout << localized("Created new private key with a public key of: ") << fc::json::to_pretty_string(v) << std::endl;
   });

//名单钱包
   auto listWallet = wallet->add_subcommand("list", localized("List opened wallets, * = unlocked"), false);
   listWallet->set_callback([] {
      std::cout << localized("Wallets:") << std::endl;
      const auto& v = call(wallet_url, wallet_list);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

//列表键
   auto listKeys = wallet->add_subcommand("keys", localized("List of public keys from all unlocked wallets."), false);
   listKeys->set_callback([] {
      const auto& v = call(wallet_url, wallet_public_keys);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

//列出私钥
   auto listPrivKeys = wallet->add_subcommand("private_keys", localized("List of private keys from an unlocked wallet in wif or PVT_R1 format."), false);
   listPrivKeys->add_option("-n,--name", wallet_name, localized("The name of the wallet to list keys from"), true);
   listPrivKeys->add_option("--password", wallet_pw, localized("The password returned by wallet create"));
   listPrivKeys->set_callback([&wallet_name, &wallet_pw] {
      prompt_for_wallet_password(wallet_pw, wallet_name);
      fc::variants vs = {fc::variant(wallet_name), fc::variant(wallet_pw)};
      const auto& v = call(wallet_url, wallet_list_keys, vs);
      std::cout << fc::json::to_pretty_string(v) << std::endl;
   });

   auto stopKeosd = wallet->add_subcommand("stop", localized("Stop keosd (doesn't work with nodeos)."), false);
   stopKeosd->set_callback([] {
      const auto& v = call(wallet_url, keosd_stop);
if ( !v.is_object() || v.get_object().size() != 0 ) { //成功时，keosd用空对象响应
         std::cerr << fc::json::to_pretty_string(v) << std::endl;
      } else {
         std::cout << "OK" << std::endl;
      }
   });

//符号子命令
   string trx_json_to_sign;
   string str_private_key;
   string str_chain_id;
   bool push_trx = false;

   auto sign = app.add_subcommand("sign", localized("Sign a transaction"), false);
   sign->add_option("transaction", trx_json_to_sign,
                                 localized("The JSON string or filename defining the transaction to sign"), true)->required();
   sign->add_option("-k,--private-key", str_private_key, localized("The private key that will be used to sign the transaction"));
   sign->add_option("-c,--chain-id", str_chain_id, localized("The chain id that will be used to sign the transaction"));
   sign->add_flag( "-p,--push-transaction", push_trx, localized("Push transaction after signing"));

   sign->set_callback([&] {
      signed_transaction trx = json_from_file_or_string(trx_json_to_sign).as<signed_transaction>();

      fc::optional<chain_id_type> chain_id;

      if( str_chain_id.size() == 0 ) {
         ilog( "grabbing chain_id from nodeos" );
         auto info = get_info();
         chain_id = info.chain_id;
      } else {
         chain_id = chain_id_type(str_chain_id);
      }

      if( str_private_key.size() == 0 ) {
         std::cerr << localized("private key: ");
         fc::set_console_echo(false);
         std::getline( std::cin, str_private_key, '\n' );
         fc::set_console_echo(true);
      }

      auto priv_key = private_key_type(str_private_key);
      trx.sign(priv_key, *chain_id);

      if(push_trx) {
         auto trx_result = call(push_txn_func, packed_transaction(trx, packed_transaction::none));
         std::cout << fc::json::to_pretty_string(trx_result) << std::endl;
      } else {
         std::cout << fc::json::to_pretty_string(trx) << std::endl;
      }
   });

//推子命令
   auto push = app.add_subcommand("push", localized("Push arbitrary transactions to the blockchain"), false);
   push->require_subcommand();

//推动行动
   string contract_account;
   string action;
   string data;
   vector<string> permissions;
   auto actionsSubcommand = push->add_subcommand("action", localized("Push a transaction with a single action"));
   actionsSubcommand->fallthrough(false);
   actionsSubcommand->add_option("account", contract_account,
                                 localized("The account providing the contract to execute"), true)->required();
   actionsSubcommand->add_option("action", action,
                                 localized("A JSON string or filename defining the action to execute on the contract"), true)->required();
   actionsSubcommand->add_option("data", data, localized("The arguments to the contract"))->required();

   add_standard_transaction_options(actionsSubcommand);
   actionsSubcommand->set_callback([&] {
      fc::variant action_args_var;
      if( !data.empty() ) {
         try {
            action_args_var = json_from_file_or_string(data, fc::json::relaxed_parser);
         } EOS_RETHROW_EXCEPTIONS(action_type_exception, "Fail to parse action JSON data='${data}'", ("data", data))
      }
      auto accountPermissions = get_account_permissions(tx_permission);

      send_actions({chain::action{accountPermissions, contract_account, action, variant_to_bin( contract_account, action, action_args_var ) }});
   });

//推送事务
   string trx_to_push;
   auto trxSubcommand = push->add_subcommand("transaction", localized("Push an arbitrary JSON transaction"));
   trxSubcommand->add_option("transaction", trx_to_push, localized("The JSON string or filename defining the transaction to push"))->required();
   add_standard_transaction_options(trxSubcommand);

   trxSubcommand->set_callback([&] {
      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string(trx_to_push);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_push))
      try {
         signed_transaction trx = trx_var.as<signed_transaction>();
         std::cout << fc::json::to_pretty_string( push_transaction( trx )) << std::endl;
      } catch( fc::exception& ) {
//无法转换，请尝试通过ABI
         signed_transaction trx;
         abi_serializer::from_variant( trx_var, trx, abi_serializer_resolver, abi_serializer_max_time );
         std::cout << fc::json::to_pretty_string( push_transaction( trx )) << std::endl;
      }
   });


   string trxsJson;
   auto trxsSubcommand = push->add_subcommand("transactions", localized("Push an array of arbitrary JSON transactions"));
   trxsSubcommand->add_option("transactions", trxsJson, localized("The JSON string or filename defining the array of the transactions to push"))->required();
   trxsSubcommand->set_callback([&] {
      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string(trxsJson);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trxsJson))
      auto trxs_result = call(push_txns_func, trx_var);
      std::cout << fc::json::to_pretty_string(trxs_result) << std::endl;
   });


//multisig子命令
   auto msig = app.add_subcommand("multisig", localized("Multisig contract commands"), false);
   msig->require_subcommand();

//多任务建议
   string proposal_name;
   string requested_perm;
   string transaction_perm;
   string proposed_transaction;
   string proposed_contract;
   string proposed_action;
   string proposer;
   unsigned int proposal_expiration_hours = 24;
   CLI::callback_t parse_expiration_hours = [&](CLI::results_t res) -> bool {
      unsigned int value_s;
      if (res.size() == 0 || !CLI::detail::lexical_cast(res[0], value_s)) {
         return false;
      }

      proposal_expiration_hours = static_cast<uint64_t>(value_s);
      return true;
   };

   auto propose_action = msig->add_subcommand("propose", localized("Propose action"));
   add_standard_transaction_options(propose_action, "proposer@active");
   propose_action->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   propose_action->add_option("requested_permissions", requested_perm, localized("The JSON string or filename defining requested permissions"))->required();
   propose_action->add_option("trx_permissions", transaction_perm, localized("The JSON string or filename defining transaction permissions"))->required();
   propose_action->add_option("contract", proposed_contract, localized("contract to which deferred transaction should be delivered"))->required();
   propose_action->add_option("action", proposed_action, localized("action of deferred transaction"))->required();
   propose_action->add_option("data", proposed_transaction, localized("The JSON string or filename defining the action to propose"))->required();
   propose_action->add_option("proposer", proposer, localized("Account proposing the transaction"));
   propose_action->add_option("proposal_expiration", parse_expiration_hours, localized("Proposal expiration interval in hours"));

   propose_action->set_callback([&] {
      fc::variant requested_perm_var;
      try {
         requested_perm_var = json_from_file_or_string(requested_perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",requested_perm))
      fc::variant transaction_perm_var;
      try {
         transaction_perm_var = json_from_file_or_string(transaction_perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",transaction_perm))
      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string(proposed_transaction);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",proposed_transaction))
      transaction proposed_trx = trx_var.as<transaction>();
      bytes proposed_trx_serialized = variant_to_bin( proposed_contract, proposed_action, trx_var );

      vector<permission_level> reqperm;
      try {
         reqperm = requested_perm_var.as<vector<permission_level>>();
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Wrong requested permissions format: '${data}'", ("data",requested_perm_var));

      vector<permission_level> trxperm;
      try {
         trxperm = transaction_perm_var.as<vector<permission_level>>();
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Wrong transaction permissions format: '${data}'", ("data",transaction_perm_var));

      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!proposer.empty()) {
            accountPermissions = vector<permission_level>{{proposer, config::active_name}};
         } else {
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <proposer> or -p)");
         }
      }
      if (proposer.empty()) {
         proposer = name(accountPermissions.at(0).actor).to_string();
      }

      transaction trx;

      trx.expiration = fc::time_point_sec( fc::time_point::now() + fc::hours(proposal_expiration_hours) );
      trx.ref_block_num = 0;
      trx.ref_block_prefix = 0;
      trx.max_net_usage_words = 0;
      trx.max_cpu_usage_ms = 0;
      trx.delay_sec = 0;
      trx.actions = { chain::action(trxperm, name(proposed_contract), name(proposed_action), proposed_trx_serialized) };

      fc::to_variant(trx, trx_var);

      auto args = fc::mutable_variant_object()
         ("proposer", proposer )
         ("proposal_name", proposal_name)
         ("requested", requested_perm_var)
         ("trx", trx_var);

      send_actions({chain::action{accountPermissions, "eosio.msig", "propose", variant_to_bin( N(eosio.msig), N(propose), args ) }});
   });

//多任务建议交易
   auto propose_trx = msig->add_subcommand("propose_trx", localized("Propose transaction"));
   add_standard_transaction_options(propose_trx, "proposer@active");
   propose_trx->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   propose_trx->add_option("requested_permissions", requested_perm, localized("The JSON string or filename defining requested permissions"))->required();
   propose_trx->add_option("transaction", trx_to_push, localized("The JSON string or filename defining the transaction to push"))->required();
   propose_trx->add_option("proposer", proposer, localized("Account proposing the transaction"));

   propose_trx->set_callback([&] {
      fc::variant requested_perm_var;
      try {
         requested_perm_var = json_from_file_or_string(requested_perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",requested_perm))

      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string(trx_to_push);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_push))

      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!proposer.empty()) {
            accountPermissions = vector<permission_level>{{proposer, config::active_name}};
         } else {
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <proposer> or -p)");
         }
      }
      if (proposer.empty()) {
         proposer = name(accountPermissions.at(0).actor).to_string();
      }

      auto args = fc::mutable_variant_object()
         ("proposer", proposer )
         ("proposal_name", proposal_name)
         ("requested", requested_perm_var)
         ("trx", trx_var);

      send_actions({chain::action{accountPermissions, "eosio.msig", "propose", variant_to_bin( N(eosio.msig), N(propose), args ) }});
   });


//多见习复习
   bool show_approvals_in_multisig_review = false;
   auto review = msig->add_subcommand("review", localized("Review transaction"));
   review->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   review->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   review->add_flag( "--show-approvals", show_approvals_in_multisig_review, localized("Show the status of the approvals requested within the proposal") );

   review->set_callback([&] {
      const auto result1 = call(get_table_func, fc::mutable_variant_object("json", true)
                                 ("code", "eosio.msig")
                                 ("scope", proposer)
                                 ("table", "proposal")
                                 ("table_key", "")
                                 ("lower_bound", name(proposal_name).value)
                                 ("upper_bound", name(proposal_name).value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//更改为name（proposal_name）.value当cleos不再需要支持早于1.5.0的nodeos版本时
                                 ("limit", 1)
                           );
//std:：cout<<fc:：json:：to_pretty_string（result）<<std:：endl；

      const auto& rows1 = result1.get_object()["rows"].get_array();
//当cleos不再需要支持早于1.5.0的nodeos版本时，下面if语句中的条件可以简单地为rows.empty（）。
      if( rows1.empty() || rows1[0].get_object()["proposal_name"] != proposal_name ) {
         std::cerr << "Proposal not found" << std::endl;
         return;
      }

      const auto& proposal_object = rows1[0].get_object();

      enum class approval_status {
         unapproved,
         approved,
         invalidated
      };

      std::map<permission_level, std::pair<fc::time_point, approval_status>>                               all_approvals;
      std::map<eosio::account_name, std::pair<fc::time_point, vector<decltype(all_approvals)::iterator>>>  provided_approvers;

      bool new_multisig = true;
      if( show_approvals_in_multisig_review ) {
         fc::variants rows2;

         try {
            const auto& result2 = call(get_table_func, fc::mutable_variant_object("json", true)
                                       ("code", "eosio.msig")
                                       ("scope", proposer)
                                       ("table", "approvals2")
                                       ("table_key", "")
                                       ("lower_bound", name(proposal_name).value)
                                       ("upper_bound", name(proposal_name).value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//更改为name（proposal_name）.value当cleos不再需要支持早于1.5.0的nodeos版本时
                                       ("limit", 1)
                                 );
            rows2 = result2.get_object()["rows"].get_array();
         } catch( ... ) {
            new_multisig = false;
         }

         if( !rows2.empty() && rows2[0].get_object()["proposal_name"] == proposal_name ) {
            const auto& approvals_object = rows2[0].get_object();

            for( const auto& ra : approvals_object["requested_approvals"].get_array() ) {
               const auto& ra_obj = ra.get_object();
               auto pl = ra["level"].as<permission_level>();
               auto res = all_approvals.emplace( pl, std::make_pair(ra["time"].as<fc::time_point>(), approval_status::unapproved) );
            }

            for( const auto& pa : approvals_object["provided_approvals"].get_array() ) {
               const auto& pa_obj = pa.get_object();
               auto pl = pa["level"].as<permission_level>();
               auto res = all_approvals.emplace( pl, std::make_pair(pa["time"].as<fc::time_point>(), approval_status::approved) );
               provided_approvers[pl.actor].second.push_back( res.first );
            }
         } else {
            const auto result3 = call(get_table_func, fc::mutable_variant_object("json", true)
                                       ("code", "eosio.msig")
                                       ("scope", proposer)
                                       ("table", "approvals")
                                       ("table_key", "")
                                       ("lower_bound", name(proposal_name).value)
                                       ("upper_bound", name(proposal_name).value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//更改为name（proposal_name）.value当cleos不再需要支持早于1.5.0的nodeos版本时
                                       ("limit", 1)
                                 );
            const auto& rows3 = result3.get_object()["rows"].get_array();
            if( rows3.empty() || rows3[0].get_object()["proposal_name"] != proposal_name ) {
               std::cerr << "Proposal not found" << std::endl;
               return;
            }

            const auto& approvals_object = rows3[0].get_object();

            for( const auto& ra : approvals_object["requested_approvals"].get_array() ) {
               auto pl = ra.as<permission_level>();
               auto res = all_approvals.emplace( pl, std::make_pair(fc::time_point{}, approval_status::unapproved) );
            }

            for( const auto& pa : approvals_object["provided_approvals"].get_array() ) {
               auto pl = pa.as<permission_level>();
               auto res = all_approvals.emplace( pl, std::make_pair(fc::time_point{}, approval_status::approved) );
               provided_approvers[pl.actor].second.push_back( res.first );
            }
         }

         if( new_multisig ) {
            for( auto& a : provided_approvers ) {
               const auto result4 = call(get_table_func, fc::mutable_variant_object("json", true)
                                          ("code", "eosio.msig")
                                          ("scope", "eosio.msig")
                                          ("table", "invals")
                                          ("table_key", "")
                                          ("lower_bound", a.first.value)
                                          ("upper_bound", a.first.value + 1)
//保留了不太理想的上界用法，这样Cleos仍然可以使用旧的Buggy Nodeos版本。
//更改为name（proposal_name）.value当cleos不再需要支持早于1.5.0的nodeos版本时
                                          ("limit", 1)
                                    );
               const auto& rows4 = result4.get_object()["rows"].get_array();
               if( rows4.empty() || rows4[0].get_object()["account"].as<eosio::name>() != a.first ) {
                  continue;
               }

               auto invalidation_time = rows4[0].get_object()["last_invalidation_time"].as<fc::time_point>();
               a.second.first = invalidation_time;

               for( auto& itr : a.second.second ) {
                  if( invalidation_time >= itr->second.first ) {
                     itr->second.second = approval_status::invalidated;
                  }
               }
            }
         }
      }

      auto trx_hex = proposal_object["packed_transaction"].as_string();
      vector<char> trx_blob(trx_hex.size()/2);
      fc::from_hex(trx_hex, trx_blob.data(), trx_blob.size());
      transaction trx = fc::raw::unpack<transaction>(trx_blob);

      fc::mutable_variant_object obj;
      obj["proposer"] = proposer;
      obj["proposal_name"] = proposal_object["proposal_name"];
      obj["transaction_id"] = trx.id();

      for( const auto& entry : proposal_object ) {
         if( entry.key() == "proposal_name" ) continue;
         obj.set( entry.key(), entry.value() );
      }

      fc::variant trx_var;
      abi_serializer abi;
      abi.to_variant(trx, trx_var, abi_serializer_resolver, abi_serializer_max_time);
      obj["transaction"] = trx_var;

      if( show_approvals_in_multisig_review ) {
         fc::variants approvals;

         for( const auto& approval : all_approvals ) {
            fc::mutable_variant_object approval_obj;
            approval_obj["level"] = approval.first;
            switch( approval.second.second ) {
               case approval_status::unapproved:
               {
                  approval_obj["status"] = "unapproved";
                  if( approval.second.first != fc::time_point{} ) {
                     approval_obj["last_unapproval_time"] = approval.second.first;
                  }
               }
               break;
               case approval_status::approved:
               {
                  approval_obj["status"] = "approved";
                  if( new_multisig ) {
                     approval_obj["last_approval_time"] = approval.second.first;
                  }
               }
               break;
               case approval_status::invalidated:
               {
                  approval_obj["status"] = "invalidated";
                  approval_obj["last_approval_time"] = approval.second.first;
                  approval_obj["invalidation_time"] = provided_approvers[approval.first.actor].first;
               }
               break;
            }

            approvals.push_back( std::move(approval_obj) );
         }

         obj["approvals"] = std::move(approvals);
      }

      std::cout << fc::json::to_pretty_string(obj) << std::endl;
   });

   string perm;
   string proposal_hash;
   auto approve_or_unapprove = [&](const string& action) {
      fc::variant perm_var;
      try {
         perm_var = json_from_file_or_string(perm);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse permissions JSON '${data}'", ("data",perm))

      auto args = fc::mutable_variant_object()
         ("proposer", proposer)
         ("proposal_name", proposal_name)
         ("level", perm_var);

      if( proposal_hash.size() ) {
         args("proposal_hash", proposal_hash);
      }

      auto accountPermissions = get_account_permissions(tx_permission, {proposer,config::active_name});
      send_actions({chain::action{accountPermissions, "eosio.msig", action, variant_to_bin( N(eosio.msig), action, args ) }});
   };

//多任务批准
   auto approve = msig->add_subcommand("approve", localized("Approve proposed transaction"));
   add_standard_transaction_options(approve, "proposer@active");
   approve->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   approve->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   approve->add_option("permissions", perm, localized("The JSON string of filename defining approving permissions"))->required();
   approve->add_option("proposal_hash", proposal_hash, localized("Hash of proposed transaction (i.e. transaction ID) to optionally enforce as a condition of the approval"));
   approve->set_callback([&] { approve_or_unapprove("approve"); });

//multisig取消审批
   auto unapprove = msig->add_subcommand("unapprove", localized("Unapprove proposed transaction"));
   add_standard_transaction_options(unapprove, "proposer@active");
   unapprove->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   unapprove->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   unapprove->add_option("permissions", perm, localized("The JSON string of filename defining approving permissions"))->required();
   unapprove->set_callback([&] { approve_or_unapprove("unapprove"); });

//多重签名失效
   string invalidator;
   auto invalidate = msig->add_subcommand("invalidate", localized("Invalidate all multisig approvals of an account"));
   add_standard_transaction_options(invalidate, "invalidator@active");
   invalidate->add_option("invalidator", invalidator, localized("invalidator name (string)"))->required();
   invalidate->set_callback([&] {
      auto args = fc::mutable_variant_object()
         ("account", invalidator);

      auto accountPermissions = get_account_permissions(tx_permission, {invalidator,config::active_name});
      send_actions({chain::action{accountPermissions, "eosio.msig", "invalidate", variant_to_bin( N(eosio.msig), "invalidate", args ) }});
   });

//多西格斯取消
   string canceler;
   auto cancel = msig->add_subcommand("cancel", localized("Cancel proposed transaction"));
   add_standard_transaction_options(cancel, "canceler@active");
   cancel->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   cancel->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   cancel->add_option("canceler", canceler, localized("canceler name (string)"));
   cancel->set_callback([&]() {
      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!canceler.empty()) {
            accountPermissions = vector<permission_level>{{canceler, config::active_name}};
         } else {
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <canceler> or -p)");
         }
      }
      if (canceler.empty()) {
         canceler = name(accountPermissions.at(0).actor).to_string();
      }
      auto args = fc::mutable_variant_object()
         ("proposer", proposer)
         ("proposal_name", proposal_name)
         ("canceler", canceler);

      send_actions({chain::action{accountPermissions, "eosio.msig", "cancel", variant_to_bin( N(eosio.msig), N(cancel), args ) }});
      }
   );

//多执行程序
   string executer;
   auto exec = msig->add_subcommand("exec", localized("Execute proposed transaction"));
   add_standard_transaction_options(exec, "executer@active");
   exec->add_option("proposer", proposer, localized("proposer name (string)"))->required();
   exec->add_option("proposal_name", proposal_name, localized("proposal name (string)"))->required();
   exec->add_option("executer", executer, localized("account paying for execution (string)"));
   exec->set_callback([&] {
      auto accountPermissions = get_account_permissions(tx_permission);
      if (accountPermissions.empty()) {
         if (!executer.empty()) {
            accountPermissions = vector<permission_level>{{executer, config::active_name}};
         } else {
            EOS_THROW(missing_auth_exception, "Authority is not provided (either by multisig parameter <executer> or -p)");
         }
      }
      if (executer.empty()) {
         executer = name(accountPermissions.at(0).actor).to_string();
      }

      auto args = fc::mutable_variant_object()
         ("proposer", proposer )
         ("proposal_name", proposal_name)
         ("executer", executer);

      send_actions({chain::action{accountPermissions, "eosio.msig", "exec", variant_to_bin( N(eosio.msig), N(exec), args ) }});
      }
   );

//包子命令
   auto wrap = app.add_subcommand("wrap", localized("Wrap contract commands"), false);
   wrap->require_subcommand();

//包执行程序
   string wrap_con = "eosio.wrap";
   executer = "";
   string trx_to_exec;
   auto wrap_exec = wrap->add_subcommand("exec", localized("Execute a transaction while bypassing authorization checks"));
   add_standard_transaction_options(wrap_exec, "executer@active & --contract@active");
   wrap_exec->add_option("executer", executer, localized("Account executing the transaction and paying for the deferred transaction RAM"))->required();
   wrap_exec->add_option("transaction", trx_to_exec, localized("The JSON string or filename defining the transaction to execute"))->required();
   wrap_exec->add_option("--contract,-c", wrap_con, localized("The account which controls the wrap contract"));

   wrap_exec->set_callback([&] {
      fc::variant trx_var;
      try {
         trx_var = json_from_file_or_string(trx_to_exec);
      } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_exec))

      auto accountPermissions = get_account_permissions(tx_permission);
      if( accountPermissions.empty() ) {
         accountPermissions = vector<permission_level>{{executer, config::active_name}, {wrap_con, config::active_name}};
      }

      auto args = fc::mutable_variant_object()
         ("executer", executer )
         ("trx", trx_var);

      send_actions({chain::action{accountPermissions, wrap_con, "exec", variant_to_bin( wrap_con, N(exec), args ) }});
   });

//系统子命令
   auto system = app.add_subcommand("system", localized("Send eosio.system contract action to the blockchain."), false);
   system->require_subcommand();

   /*o createAccountSystem=create_account_子命令（system，false/*simple*/）；
   auto register producer=register_producer_子命令（system）；
   auto unregister producer=unregister_producer_子命令（system）；

   auto vote producer=system->add_子命令（“vote producer”，本地化（“投票给生产商”））；
   voteProducer->Require_subcommand（）；
   auto vote proxy=vote_producer_proxy_子命令（vote producer）；
   auto vote producers=投票_producers_子命令（voteproducer）；
   auto approveProducer=批准生产商_子命令（voteProducer）；
   auto unapproved producer=unapprove_producer_子命令（voteproducer）；

   auto-listproducer=列出“生产者”子命令（system）；

   auto delegateBandwidth=delegate_bandwidth_子命令（system）；
   auto undelegateBandWidth=undelegate_bandwidth_子命令（system）；
   auto listBandwidth=list_BW_子命令（system）；
   auto bidname=bidname_子命令（system）；
   auto bidname info=bidname_info_子命令（system）；

   auto biyram=buyram_子命令（系统）；
   auto sellram=sellram_子命令（系统）；

   auto claimrewards=claimrewards_子命令（系统）；

   auto regproxy=regproxy_子命令（system）；
   auto unregroxy=unregroxy_子命令（系统）；

   auto canceldelay=canceldelay_子命令（system）；

   尝试{
       app.parse（argc，argv）；
   catch（const cli:：parserror&e）
       返回应用程序退出（e）；
   catch（const解释了_exception&e）
      返回1；
   catch（连接_exception&e）
      if（详细错误）
         ELOG（“连接错误：$e”，“e”，e.to_detail_string（））；
      }
      返回1；
   catch（const fc:：exception&e）
      //如果存在错误代码，则尝试提取错误代码
      如果（！）打印“已识别的”错误（E，详细的“错误”）；
         //无法识别错误
         如果（！）打印“帮助”文本（E）详细“错误”
            ELOG（“失败，错误为：$e”，“e”，详细错误？e.to_detail_string（）：e.to_string（））；
         }
      }
      返回1；
   }

   返回0；
}
